diff options
author | Joel Kronqvist <work.joelkronqvist@pm.me> | 2022-03-11 20:46:06 +0200 |
---|---|---|
committer | Joel Kronqvist <work.joelkronqvist@pm.me> | 2022-03-11 20:46:06 +0200 |
commit | 080c5819d87b933816d724a83f3bf4f1686770a7 (patch) | |
tree | 4a2ccc68b27edf7d4cbc586c932cc7542b655e19 /node_modules/prompts | |
parent | 5ac7049a9d30733165cc212dee308163c2a14644 (diff) | |
parent | d003b82235a9329f912522a2f70aa950dfce4998 (diff) | |
download | LYLLRuoka-080c5819d87b933816d724a83f3bf4f1686770a7.tar.gz LYLLRuoka-080c5819d87b933816d724a83f3bf4f1686770a7.zip |
Merge branch 'master' of https://github.com/JoelHMikael/FoodJS
Updating remote changes
Diffstat (limited to 'node_modules/prompts')
68 files changed, 6524 insertions, 0 deletions
diff --git a/node_modules/prompts/dist/dateparts/datepart.js b/node_modules/prompts/dist/dateparts/datepart.js new file mode 100644 index 0000000..b954c5e --- /dev/null +++ b/node_modules/prompts/dist/dateparts/datepart.js @@ -0,0 +1,39 @@ +'use strict'; + +class DatePart { + constructor({ + token, + date, + parts, + locales + }) { + this.token = token; + this.date = date || new Date(); + this.parts = parts || [this]; + this.locales = locales || {}; + } + + up() {} + + down() {} + + next() { + const currentIdx = this.parts.indexOf(this); + return this.parts.find((part, idx) => idx > currentIdx && part instanceof DatePart); + } + + setTo(val) {} + + prev() { + let parts = [].concat(this.parts).reverse(); + const currentIdx = parts.indexOf(this); + return parts.find((part, idx) => idx > currentIdx && part instanceof DatePart); + } + + toString() { + return String(this.date); + } + +} + +module.exports = DatePart;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/day.js b/node_modules/prompts/dist/dateparts/day.js new file mode 100644 index 0000000..a525e92 --- /dev/null +++ b/node_modules/prompts/dist/dateparts/day.js @@ -0,0 +1,35 @@ +'use strict'; + +const DatePart = require('./datepart'); + +const pos = n => { + n = n % 10; + return n === 1 ? 'st' : n === 2 ? 'nd' : n === 3 ? 'rd' : 'th'; +}; + +class Day extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setDate(this.date.getDate() + 1); + } + + down() { + this.date.setDate(this.date.getDate() - 1); + } + + setTo(val) { + this.date.setDate(parseInt(val.substr(-2))); + } + + toString() { + let date = this.date.getDate(); + let day = this.date.getDay(); + return this.token === 'DD' ? String(date).padStart(2, '0') : this.token === 'Do' ? date + pos(date) : this.token === 'd' ? day + 1 : this.token === 'ddd' ? this.locales.weekdaysShort[day] : this.token === 'dddd' ? this.locales.weekdays[day] : date; + } + +} + +module.exports = Day;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/hours.js b/node_modules/prompts/dist/dateparts/hours.js new file mode 100644 index 0000000..7743632 --- /dev/null +++ b/node_modules/prompts/dist/dateparts/hours.js @@ -0,0 +1,30 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Hours extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setHours(this.date.getHours() + 1); + } + + down() { + this.date.setHours(this.date.getHours() - 1); + } + + setTo(val) { + this.date.setHours(parseInt(val.substr(-2))); + } + + toString() { + let hours = this.date.getHours(); + if (/h/.test(this.token)) hours = hours % 12 || 12; + return this.token.length > 1 ? String(hours).padStart(2, '0') : hours; + } + +} + +module.exports = Hours;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/index.js b/node_modules/prompts/dist/dateparts/index.js new file mode 100644 index 0000000..754516e --- /dev/null +++ b/node_modules/prompts/dist/dateparts/index.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + DatePart: require('./datepart'), + Meridiem: require('./meridiem'), + Day: require('./day'), + Hours: require('./hours'), + Milliseconds: require('./milliseconds'), + Minutes: require('./minutes'), + Month: require('./month'), + Seconds: require('./seconds'), + Year: require('./year') +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/meridiem.js b/node_modules/prompts/dist/dateparts/meridiem.js new file mode 100644 index 0000000..5bc8dd7 --- /dev/null +++ b/node_modules/prompts/dist/dateparts/meridiem.js @@ -0,0 +1,25 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Meridiem extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setHours((this.date.getHours() + 12) % 24); + } + + down() { + this.up(); + } + + toString() { + let meridiem = this.date.getHours() > 12 ? 'pm' : 'am'; + return /\A/.test(this.token) ? meridiem.toUpperCase() : meridiem; + } + +} + +module.exports = Meridiem;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/milliseconds.js b/node_modules/prompts/dist/dateparts/milliseconds.js new file mode 100644 index 0000000..3440e3a --- /dev/null +++ b/node_modules/prompts/dist/dateparts/milliseconds.js @@ -0,0 +1,28 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Milliseconds extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setMilliseconds(this.date.getMilliseconds() + 1); + } + + down() { + this.date.setMilliseconds(this.date.getMilliseconds() - 1); + } + + setTo(val) { + this.date.setMilliseconds(parseInt(val.substr(-this.token.length))); + } + + toString() { + return String(this.date.getMilliseconds()).padStart(4, '0').substr(0, this.token.length); + } + +} + +module.exports = Milliseconds;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/minutes.js b/node_modules/prompts/dist/dateparts/minutes.js new file mode 100644 index 0000000..2b8ef1f --- /dev/null +++ b/node_modules/prompts/dist/dateparts/minutes.js @@ -0,0 +1,29 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Minutes extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setMinutes(this.date.getMinutes() + 1); + } + + down() { + this.date.setMinutes(this.date.getMinutes() - 1); + } + + setTo(val) { + this.date.setMinutes(parseInt(val.substr(-2))); + } + + toString() { + let m = this.date.getMinutes(); + return this.token.length > 1 ? String(m).padStart(2, '0') : m; + } + +} + +module.exports = Minutes;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/month.js b/node_modules/prompts/dist/dateparts/month.js new file mode 100644 index 0000000..f9d4e13 --- /dev/null +++ b/node_modules/prompts/dist/dateparts/month.js @@ -0,0 +1,31 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Month extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setMonth(this.date.getMonth() + 1); + } + + down() { + this.date.setMonth(this.date.getMonth() - 1); + } + + setTo(val) { + val = parseInt(val.substr(-2)) - 1; + this.date.setMonth(val < 0 ? 0 : val); + } + + toString() { + let month = this.date.getMonth(); + let tl = this.token.length; + return tl === 2 ? String(month + 1).padStart(2, '0') : tl === 3 ? this.locales.monthsShort[month] : tl === 4 ? this.locales.months[month] : String(month + 1); + } + +} + +module.exports = Month;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/seconds.js b/node_modules/prompts/dist/dateparts/seconds.js new file mode 100644 index 0000000..e16f030 --- /dev/null +++ b/node_modules/prompts/dist/dateparts/seconds.js @@ -0,0 +1,29 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Seconds extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setSeconds(this.date.getSeconds() + 1); + } + + down() { + this.date.setSeconds(this.date.getSeconds() - 1); + } + + setTo(val) { + this.date.setSeconds(parseInt(val.substr(-2))); + } + + toString() { + let s = this.date.getSeconds(); + return this.token.length > 1 ? String(s).padStart(2, '0') : s; + } + +} + +module.exports = Seconds;
\ No newline at end of file diff --git a/node_modules/prompts/dist/dateparts/year.js b/node_modules/prompts/dist/dateparts/year.js new file mode 100644 index 0000000..cd62677 --- /dev/null +++ b/node_modules/prompts/dist/dateparts/year.js @@ -0,0 +1,29 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Year extends DatePart { + constructor(opts = {}) { + super(opts); + } + + up() { + this.date.setFullYear(this.date.getFullYear() + 1); + } + + down() { + this.date.setFullYear(this.date.getFullYear() - 1); + } + + setTo(val) { + this.date.setFullYear(val.substr(-4)); + } + + toString() { + let year = String(this.date.getFullYear()).padStart(4, '0'); + return this.token.length === 2 ? year.substr(-2) : year; + } + +} + +module.exports = Year;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/autocomplete.js b/node_modules/prompts/dist/elements/autocomplete.js new file mode 100644 index 0000000..60e41bc --- /dev/null +++ b/node_modules/prompts/dist/elements/autocomplete.js @@ -0,0 +1,285 @@ +'use strict'; + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('sisteransi'), + erase = _require.erase, + cursor = _require.cursor; + +const _require2 = require('../util'), + style = _require2.style, + clear = _require2.clear, + figures = _require2.figures, + wrap = _require2.wrap, + entriesToDisplay = _require2.entriesToDisplay; + +const getVal = (arr, i) => arr[i] && (arr[i].value || arr[i].title || arr[i]); + +const getTitle = (arr, i) => arr[i] && (arr[i].title || arr[i].value || arr[i]); + +const getIndex = (arr, valOrTitle) => { + const index = arr.findIndex(el => el.value === valOrTitle || el.title === valOrTitle); + return index > -1 ? index : undefined; +}; +/** + * TextPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of auto-complete choices objects + * @param {Function} [opts.suggest] Filter function. Defaults to sort by title + * @param {Number} [opts.limit=10] Max number of results to show + * @param {Number} [opts.cursor=0] Cursor start position + * @param {String} [opts.style='default'] Render style + * @param {String} [opts.fallback] Fallback message - initial to default value + * @param {String} [opts.initial] Index of the default value + * @param {Boolean} [opts.clearFirst] The first ESCAPE keypress will clear the input + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.noMatches] The no matches found label + */ + + +class AutocompletePrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.msg = opts.message; + this.suggest = opts.suggest; + this.choices = opts.choices; + this.initial = typeof opts.initial === 'number' ? opts.initial : getIndex(opts.choices, opts.initial); + this.select = this.initial || opts.cursor || 0; + this.i18n = { + noMatches: opts.noMatches || 'no matches found' + }; + this.fallback = opts.fallback || this.initial; + this.clearFirst = opts.clearFirst || false; + this.suggestions = []; + this.input = ''; + this.limit = opts.limit || 10; + this.cursor = 0; + this.transform = style.render(opts.style); + this.scale = this.transform.scale; + this.render = this.render.bind(this); + this.complete = this.complete.bind(this); + this.clear = clear('', this.out.columns); + this.complete(this.render); + this.render(); + } + + set fallback(fb) { + this._fb = Number.isSafeInteger(parseInt(fb)) ? parseInt(fb) : fb; + } + + get fallback() { + let choice; + if (typeof this._fb === 'number') choice = this.choices[this._fb];else if (typeof this._fb === 'string') choice = { + title: this._fb + }; + return choice || this._fb || { + title: this.i18n.noMatches + }; + } + + moveSelect(i) { + this.select = i; + if (this.suggestions.length > 0) this.value = getVal(this.suggestions, i);else this.value = this.fallback.value; + this.fire(); + } + + complete(cb) { + var _this = this; + + return _asyncToGenerator(function* () { + const p = _this.completing = _this.suggest(_this.input, _this.choices); + + const suggestions = yield p; + if (_this.completing !== p) return; + _this.suggestions = suggestions.map((s, i, arr) => ({ + title: getTitle(arr, i), + value: getVal(arr, i), + description: s.description + })); + _this.completing = false; + const l = Math.max(suggestions.length - 1, 0); + + _this.moveSelect(Math.min(l, _this.select)); + + cb && cb(); + })(); + } + + reset() { + this.input = ''; + this.complete(() => { + this.moveSelect(this.initial !== void 0 ? this.initial : 0); + this.render(); + }); + this.render(); + } + + exit() { + if (this.clearFirst && this.input.length > 0) { + this.reset(); + } else { + this.done = this.exited = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + } + + abort() { + this.done = this.aborted = true; + this.exited = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + this.done = true; + this.aborted = this.exited = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + _(c, key) { + let s1 = this.input.slice(0, this.cursor); + let s2 = this.input.slice(this.cursor); + this.input = `${s1}${c}${s2}`; + this.cursor = s1.length + 1; + this.complete(this.render); + this.render(); + } + + delete() { + if (this.cursor === 0) return this.bell(); + let s1 = this.input.slice(0, this.cursor - 1); + let s2 = this.input.slice(this.cursor); + this.input = `${s1}${s2}`; + this.complete(this.render); + this.cursor = this.cursor - 1; + this.render(); + } + + deleteForward() { + if (this.cursor * this.scale >= this.rendered.length) return this.bell(); + let s1 = this.input.slice(0, this.cursor); + let s2 = this.input.slice(this.cursor + 1); + this.input = `${s1}${s2}`; + this.complete(this.render); + this.render(); + } + + first() { + this.moveSelect(0); + this.render(); + } + + last() { + this.moveSelect(this.suggestions.length - 1); + this.render(); + } + + up() { + if (this.select === 0) { + this.moveSelect(this.suggestions.length - 1); + } else { + this.moveSelect(this.select - 1); + } + + this.render(); + } + + down() { + if (this.select === this.suggestions.length - 1) { + this.moveSelect(0); + } else { + this.moveSelect(this.select + 1); + } + + this.render(); + } + + next() { + if (this.select === this.suggestions.length - 1) { + this.moveSelect(0); + } else this.moveSelect(this.select + 1); + + this.render(); + } + + nextPage() { + this.moveSelect(Math.min(this.select + this.limit, this.suggestions.length - 1)); + this.render(); + } + + prevPage() { + this.moveSelect(Math.max(this.select - this.limit, 0)); + this.render(); + } + + left() { + if (this.cursor <= 0) return this.bell(); + this.cursor = this.cursor - 1; + this.render(); + } + + right() { + if (this.cursor * this.scale >= this.rendered.length) return this.bell(); + this.cursor = this.cursor + 1; + this.render(); + } + + renderOption(v, hovered, isStart, isEnd) { + let desc; + let prefix = isStart ? figures.arrowUp : isEnd ? figures.arrowDown : ' '; + let title = hovered ? color.cyan().underline(v.title) : v.title; + prefix = (hovered ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; + + if (v.description) { + desc = ` - ${v.description}`; + + if (prefix.length + title.length + desc.length >= this.out.columns || v.description.split(/\r?\n/).length > 1) { + desc = '\n' + wrap(v.description, { + margin: 3, + width: this.out.columns + }); + } + } + + return prefix + ' ' + title + color.gray(desc || ''); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + let _entriesToDisplay = entriesToDisplay(this.select, this.choices.length, this.limit), + startIndex = _entriesToDisplay.startIndex, + endIndex = _entriesToDisplay.endIndex; + + this.outputText = [style.symbol(this.done, this.aborted, this.exited), color.bold(this.msg), style.delimiter(this.completing), this.done && this.suggestions[this.select] ? this.suggestions[this.select].title : this.rendered = this.transform.render(this.input)].join(' '); + + if (!this.done) { + const suggestions = this.suggestions.slice(startIndex, endIndex).map((item, i) => this.renderOption(item, this.select === i + startIndex, i === 0 && startIndex > 0, i + startIndex === endIndex - 1 && endIndex < this.choices.length)).join('\n'); + this.outputText += `\n` + (suggestions || color.gray(this.fallback.title)); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText); + } + +} + +module.exports = AutocompletePrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/autocompleteMultiselect.js b/node_modules/prompts/dist/elements/autocompleteMultiselect.js new file mode 100644 index 0000000..ac4af0d --- /dev/null +++ b/node_modules/prompts/dist/elements/autocompleteMultiselect.js @@ -0,0 +1,201 @@ +'use strict'; + +const color = require('kleur'); + +const _require = require('sisteransi'), + cursor = _require.cursor; + +const MultiselectPrompt = require('./multiselect'); + +const _require2 = require('../util'), + clear = _require2.clear, + style = _require2.style, + figures = _require2.figures; +/** + * MultiselectPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of choice objects + * @param {String} [opts.hint] Hint to display + * @param {String} [opts.warn] Hint shown for disabled choices + * @param {Number} [opts.max] Max choices + * @param {Number} [opts.cursor=0] Cursor start position + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ + + +class AutocompleteMultiselectPrompt extends MultiselectPrompt { + constructor(opts = {}) { + opts.overrideRender = true; + super(opts); + this.inputValue = ''; + this.clear = clear('', this.out.columns); + this.filteredOptions = this.value; + this.render(); + } + + last() { + this.cursor = this.filteredOptions.length - 1; + this.render(); + } + + next() { + this.cursor = (this.cursor + 1) % this.filteredOptions.length; + this.render(); + } + + up() { + if (this.cursor === 0) { + this.cursor = this.filteredOptions.length - 1; + } else { + this.cursor--; + } + + this.render(); + } + + down() { + if (this.cursor === this.filteredOptions.length - 1) { + this.cursor = 0; + } else { + this.cursor++; + } + + this.render(); + } + + left() { + this.filteredOptions[this.cursor].selected = false; + this.render(); + } + + right() { + if (this.value.filter(e => e.selected).length >= this.maxChoices) return this.bell(); + this.filteredOptions[this.cursor].selected = true; + this.render(); + } + + delete() { + if (this.inputValue.length) { + this.inputValue = this.inputValue.substr(0, this.inputValue.length - 1); + this.updateFilteredOptions(); + } + } + + updateFilteredOptions() { + const currentHighlight = this.filteredOptions[this.cursor]; + this.filteredOptions = this.value.filter(v => { + if (this.inputValue) { + if (typeof v.title === 'string') { + if (v.title.toLowerCase().includes(this.inputValue.toLowerCase())) { + return true; + } + } + + if (typeof v.value === 'string') { + if (v.value.toLowerCase().includes(this.inputValue.toLowerCase())) { + return true; + } + } + + return false; + } + + return true; + }); + const newHighlightIndex = this.filteredOptions.findIndex(v => v === currentHighlight); + this.cursor = newHighlightIndex < 0 ? 0 : newHighlightIndex; + this.render(); + } + + handleSpaceToggle() { + const v = this.filteredOptions[this.cursor]; + + if (v.selected) { + v.selected = false; + this.render(); + } else if (v.disabled || this.value.filter(e => e.selected).length >= this.maxChoices) { + return this.bell(); + } else { + v.selected = true; + this.render(); + } + } + + handleInputChange(c) { + this.inputValue = this.inputValue + c; + this.updateFilteredOptions(); + } + + _(c, key) { + if (c === ' ') { + this.handleSpaceToggle(); + } else { + this.handleInputChange(c); + } + } + + renderInstructions() { + if (this.instructions === undefined || this.instructions) { + if (typeof this.instructions === 'string') { + return this.instructions; + } + + return ` +Instructions: + ${figures.arrowUp}/${figures.arrowDown}: Highlight option + ${figures.arrowLeft}/${figures.arrowRight}/[space]: Toggle selection + [a,b,c]/delete: Filter choices + enter/return: Complete answer +`; + } + + return ''; + } + + renderCurrentInput() { + return ` +Filtered results for: ${this.inputValue ? this.inputValue : color.gray('Enter something to filter')}\n`; + } + + renderOption(cursor, v, i) { + let title; + if (v.disabled) title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title);else title = cursor === i ? color.cyan().underline(v.title) : v.title; + return (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + title; + } + + renderDoneOrInstructions() { + if (this.done) { + return this.value.filter(e => e.selected).map(v => v.title).join(', '); + } + + const output = [color.gray(this.hint), this.renderInstructions(), this.renderCurrentInput()]; + + if (this.filteredOptions.length && this.filteredOptions[this.cursor].disabled) { + output.push(color.yellow(this.warn)); + } + + return output.join(' '); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + super.render(); // print prompt + + let prompt = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(false), this.renderDoneOrInstructions()].join(' '); + + if (this.showMinError) { + prompt += color.red(`You must select a minimum of ${this.minSelected} choices.`); + this.showMinError = false; + } + + prompt += this.renderOptions(this.filteredOptions); + this.out.write(this.clear + prompt); + this.clear = clear(prompt, this.out.columns); + } + +} + +module.exports = AutocompleteMultiselectPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/confirm.js b/node_modules/prompts/dist/elements/confirm.js new file mode 100644 index 0000000..6c342d2 --- /dev/null +++ b/node_modules/prompts/dist/elements/confirm.js @@ -0,0 +1,93 @@ +"use strict"; + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('../util'), + style = _require.style, + clear = _require.clear; + +const _require2 = require('sisteransi'), + erase = _require2.erase, + cursor = _require2.cursor; +/** + * ConfirmPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Boolean} [opts.initial] Default value (true/false) + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.yes] The "Yes" label + * @param {String} [opts.yesOption] The "Yes" option when choosing between yes/no + * @param {String} [opts.no] The "No" label + * @param {String} [opts.noOption] The "No" option when choosing between yes/no + */ + + +class ConfirmPrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.msg = opts.message; + this.value = opts.initial; + this.initialValue = !!opts.initial; + this.yesMsg = opts.yes || 'yes'; + this.yesOption = opts.yesOption || '(Y/n)'; + this.noMsg = opts.no || 'no'; + this.noOption = opts.noOption || '(y/N)'; + this.render(); + } + + reset() { + this.value = this.initialValue; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + this.value = this.value || false; + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + _(c, key) { + if (c.toLowerCase() === 'y') { + this.value = true; + return this.submit(); + } + + if (c.toLowerCase() === 'n') { + this.value = false; + return this.submit(); + } + + return this.bell(); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(this.done), this.done ? this.value ? this.yesMsg : this.noMsg : color.gray(this.initialValue ? this.yesOption : this.noOption)].join(' '); + this.out.write(erase.line + cursor.to(0) + this.outputText); + } + +} + +module.exports = ConfirmPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/date.js b/node_modules/prompts/dist/elements/date.js new file mode 100644 index 0000000..0b254f8 --- /dev/null +++ b/node_modules/prompts/dist/elements/date.js @@ -0,0 +1,250 @@ +'use strict'; + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('../util'), + style = _require.style, + clear = _require.clear, + figures = _require.figures; + +const _require2 = require('sisteransi'), + erase = _require2.erase, + cursor = _require2.cursor; + +const _require3 = require('../dateparts'), + DatePart = _require3.DatePart, + Meridiem = _require3.Meridiem, + Day = _require3.Day, + Hours = _require3.Hours, + Milliseconds = _require3.Milliseconds, + Minutes = _require3.Minutes, + Month = _require3.Month, + Seconds = _require3.Seconds, + Year = _require3.Year; + +const regex = /\\(.)|"((?:\\["\\]|[^"])+)"|(D[Do]?|d{3,4}|d)|(M{1,4})|(YY(?:YY)?)|([aA])|([Hh]{1,2})|(m{1,2})|(s{1,2})|(S{1,4})|./g; +const regexGroups = { + 1: ({ + token + }) => token.replace(/\\(.)/g, '$1'), + 2: opts => new Day(opts), + // Day // TODO + 3: opts => new Month(opts), + // Month + 4: opts => new Year(opts), + // Year + 5: opts => new Meridiem(opts), + // AM/PM // TODO (special) + 6: opts => new Hours(opts), + // Hours + 7: opts => new Minutes(opts), + // Minutes + 8: opts => new Seconds(opts), + // Seconds + 9: opts => new Milliseconds(opts) // Fractional seconds + +}; +const dfltLocales = { + months: 'January,February,March,April,May,June,July,August,September,October,November,December'.split(','), + monthsShort: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), + weekdays: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), + weekdaysShort: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(',') +}; +/** + * DatePrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Number} [opts.initial] Index of default value + * @param {String} [opts.mask] The format mask + * @param {object} [opts.locales] The date locales + * @param {String} [opts.error] The error message shown on invalid value + * @param {Function} [opts.validate] Function to validate the submitted value + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ + +class DatePrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.msg = opts.message; + this.cursor = 0; + this.typed = ''; + this.locales = Object.assign(dfltLocales, opts.locales); + this._date = opts.initial || new Date(); + this.errorMsg = opts.error || 'Please Enter A Valid Value'; + + this.validator = opts.validate || (() => true); + + this.mask = opts.mask || 'YYYY-MM-DD HH:mm:ss'; + this.clear = clear('', this.out.columns); + this.render(); + } + + get value() { + return this.date; + } + + get date() { + return this._date; + } + + set date(date) { + if (date) this._date.setTime(date.getTime()); + } + + set mask(mask) { + let result; + this.parts = []; + + while (result = regex.exec(mask)) { + let match = result.shift(); + let idx = result.findIndex(gr => gr != null); + this.parts.push(idx in regexGroups ? regexGroups[idx]({ + token: result[idx] || match, + date: this.date, + parts: this.parts, + locales: this.locales + }) : result[idx] || match); + } + + let parts = this.parts.reduce((arr, i) => { + if (typeof i === 'string' && typeof arr[arr.length - 1] === 'string') arr[arr.length - 1] += i;else arr.push(i); + return arr; + }, []); + this.parts.splice(0); + this.parts.push(...parts); + this.reset(); + } + + moveCursor(n) { + this.typed = ''; + this.cursor = n; + this.fire(); + } + + reset() { + this.moveCursor(this.parts.findIndex(p => p instanceof DatePart)); + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.error = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + validate() { + var _this = this; + + return _asyncToGenerator(function* () { + let valid = yield _this.validator(_this.value); + + if (typeof valid === 'string') { + _this.errorMsg = valid; + valid = false; + } + + _this.error = !valid; + })(); + } + + submit() { + var _this2 = this; + + return _asyncToGenerator(function* () { + yield _this2.validate(); + + if (_this2.error) { + _this2.color = 'red'; + + _this2.fire(); + + _this2.render(); + + return; + } + + _this2.done = true; + _this2.aborted = false; + + _this2.fire(); + + _this2.render(); + + _this2.out.write('\n'); + + _this2.close(); + })(); + } + + up() { + this.typed = ''; + this.parts[this.cursor].up(); + this.render(); + } + + down() { + this.typed = ''; + this.parts[this.cursor].down(); + this.render(); + } + + left() { + let prev = this.parts[this.cursor].prev(); + if (prev == null) return this.bell(); + this.moveCursor(this.parts.indexOf(prev)); + this.render(); + } + + right() { + let next = this.parts[this.cursor].next(); + if (next == null) return this.bell(); + this.moveCursor(this.parts.indexOf(next)); + this.render(); + } + + next() { + let next = this.parts[this.cursor].next(); + this.moveCursor(next ? this.parts.indexOf(next) : this.parts.findIndex(part => part instanceof DatePart)); + this.render(); + } + + _(c) { + if (/\d/.test(c)) { + this.typed += c; + this.parts[this.cursor].setTo(this.typed); + this.render(); + } + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); // Print prompt + + this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(false), this.parts.reduce((arr, p, idx) => arr.concat(idx === this.cursor && !this.done ? color.cyan().underline(p.toString()) : p), []).join('')].join(' '); // Print error + + if (this.error) { + this.outputText += this.errorMsg.split('\n').reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText); + } + +} + +module.exports = DatePrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/index.js b/node_modules/prompts/dist/elements/index.js new file mode 100644 index 0000000..cf0ccc1 --- /dev/null +++ b/node_modules/prompts/dist/elements/index.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + TextPrompt: require('./text'), + SelectPrompt: require('./select'), + TogglePrompt: require('./toggle'), + DatePrompt: require('./date'), + NumberPrompt: require('./number'), + MultiselectPrompt: require('./multiselect'), + AutocompletePrompt: require('./autocomplete'), + AutocompleteMultiselectPrompt: require('./autocompleteMultiselect'), + ConfirmPrompt: require('./confirm') +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/multiselect.js b/node_modules/prompts/dist/elements/multiselect.js new file mode 100644 index 0000000..2d129ea --- /dev/null +++ b/node_modules/prompts/dist/elements/multiselect.js @@ -0,0 +1,289 @@ +'use strict'; + +const color = require('kleur'); + +const _require = require('sisteransi'), + cursor = _require.cursor; + +const Prompt = require('./prompt'); + +const _require2 = require('../util'), + clear = _require2.clear, + figures = _require2.figures, + style = _require2.style, + wrap = _require2.wrap, + entriesToDisplay = _require2.entriesToDisplay; +/** + * MultiselectPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of choice objects + * @param {String} [opts.hint] Hint to display + * @param {String} [opts.warn] Hint shown for disabled choices + * @param {Number} [opts.max] Max choices + * @param {Number} [opts.cursor=0] Cursor start position + * @param {Number} [opts.optionsPerPage=10] Max options to display at once + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ + + +class MultiselectPrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.msg = opts.message; + this.cursor = opts.cursor || 0; + this.scrollIndex = opts.cursor || 0; + this.hint = opts.hint || ''; + this.warn = opts.warn || '- This option is disabled -'; + this.minSelected = opts.min; + this.showMinError = false; + this.maxChoices = opts.max; + this.instructions = opts.instructions; + this.optionsPerPage = opts.optionsPerPage || 10; + this.value = opts.choices.map((ch, idx) => { + if (typeof ch === 'string') ch = { + title: ch, + value: idx + }; + return { + title: ch && (ch.title || ch.value || ch), + description: ch && ch.description, + value: ch && (ch.value === undefined ? idx : ch.value), + selected: ch && ch.selected, + disabled: ch && ch.disabled + }; + }); + this.clear = clear('', this.out.columns); + + if (!opts.overrideRender) { + this.render(); + } + } + + reset() { + this.value.map(v => !v.selected); + this.cursor = 0; + this.fire(); + this.render(); + } + + selected() { + return this.value.filter(v => v.selected); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + const selected = this.value.filter(e => e.selected); + + if (this.minSelected && selected.length < this.minSelected) { + this.showMinError = true; + this.render(); + } else { + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + } + + first() { + this.cursor = 0; + this.render(); + } + + last() { + this.cursor = this.value.length - 1; + this.render(); + } + + next() { + this.cursor = (this.cursor + 1) % this.value.length; + this.render(); + } + + up() { + if (this.cursor === 0) { + this.cursor = this.value.length - 1; + } else { + this.cursor--; + } + + this.render(); + } + + down() { + if (this.cursor === this.value.length - 1) { + this.cursor = 0; + } else { + this.cursor++; + } + + this.render(); + } + + left() { + this.value[this.cursor].selected = false; + this.render(); + } + + right() { + if (this.value.filter(e => e.selected).length >= this.maxChoices) return this.bell(); + this.value[this.cursor].selected = true; + this.render(); + } + + handleSpaceToggle() { + const v = this.value[this.cursor]; + + if (v.selected) { + v.selected = false; + this.render(); + } else if (v.disabled || this.value.filter(e => e.selected).length >= this.maxChoices) { + return this.bell(); + } else { + v.selected = true; + this.render(); + } + } + + toggleAll() { + if (this.maxChoices !== undefined || this.value[this.cursor].disabled) { + return this.bell(); + } + + const newSelected = !this.value[this.cursor].selected; + this.value.filter(v => !v.disabled).forEach(v => v.selected = newSelected); + this.render(); + } + + _(c, key) { + if (c === ' ') { + this.handleSpaceToggle(); + } else if (c === 'a') { + this.toggleAll(); + } else { + return this.bell(); + } + } + + renderInstructions() { + if (this.instructions === undefined || this.instructions) { + if (typeof this.instructions === 'string') { + return this.instructions; + } + + return '\nInstructions:\n' + ` ${figures.arrowUp}/${figures.arrowDown}: Highlight option\n` + ` ${figures.arrowLeft}/${figures.arrowRight}/[space]: Toggle selection\n` + (this.maxChoices === undefined ? ` a: Toggle all\n` : '') + ` enter/return: Complete answer`; + } + + return ''; + } + + renderOption(cursor, v, i, arrowIndicator) { + const prefix = (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + arrowIndicator + ' '; + let title, desc; + + if (v.disabled) { + title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); + } else { + title = cursor === i ? color.cyan().underline(v.title) : v.title; + + if (cursor === i && v.description) { + desc = ` - ${v.description}`; + + if (prefix.length + title.length + desc.length >= this.out.columns || v.description.split(/\r?\n/).length > 1) { + desc = '\n' + wrap(v.description, { + margin: prefix.length, + width: this.out.columns + }); + } + } + } + + return prefix + title + color.gray(desc || ''); + } // shared with autocompleteMultiselect + + + paginateOptions(options) { + if (options.length === 0) { + return color.red('No matches for this query.'); + } + + let _entriesToDisplay = entriesToDisplay(this.cursor, options.length, this.optionsPerPage), + startIndex = _entriesToDisplay.startIndex, + endIndex = _entriesToDisplay.endIndex; + + let prefix, + styledOptions = []; + + for (let i = startIndex; i < endIndex; i++) { + if (i === startIndex && startIndex > 0) { + prefix = figures.arrowUp; + } else if (i === endIndex - 1 && endIndex < options.length) { + prefix = figures.arrowDown; + } else { + prefix = ' '; + } + + styledOptions.push(this.renderOption(this.cursor, options[i], i, prefix)); + } + + return '\n' + styledOptions.join('\n'); + } // shared with autocomleteMultiselect + + + renderOptions(options) { + if (!this.done) { + return this.paginateOptions(options); + } + + return ''; + } + + renderDoneOrInstructions() { + if (this.done) { + return this.value.filter(e => e.selected).map(v => v.title).join(', '); + } + + const output = [color.gray(this.hint), this.renderInstructions()]; + + if (this.value[this.cursor].disabled) { + output.push(color.yellow(this.warn)); + } + + return output.join(' '); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + super.render(); // print prompt + + let prompt = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(false), this.renderDoneOrInstructions()].join(' '); + + if (this.showMinError) { + prompt += color.red(`You must select a minimum of ${this.minSelected} choices.`); + this.showMinError = false; + } + + prompt += this.renderOptions(this.value); + this.out.write(this.clear + prompt); + this.clear = clear(prompt, this.out.columns); + } + +} + +module.exports = MultiselectPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/number.js b/node_modules/prompts/dist/elements/number.js new file mode 100644 index 0000000..fd144af --- /dev/null +++ b/node_modules/prompts/dist/elements/number.js @@ -0,0 +1,250 @@ +"use strict"; + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('sisteransi'), + cursor = _require.cursor, + erase = _require.erase; + +const _require2 = require('../util'), + style = _require2.style, + figures = _require2.figures, + clear = _require2.clear, + lines = _require2.lines; + +const isNumber = /[0-9]/; + +const isDef = any => any !== undefined; + +const round = (number, precision) => { + let factor = Math.pow(10, precision); + return Math.round(number * factor) / factor; +}; +/** + * NumberPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {String} [opts.style='default'] Render style + * @param {Number} [opts.initial] Default value + * @param {Number} [opts.max=+Infinity] Max value + * @param {Number} [opts.min=-Infinity] Min value + * @param {Boolean} [opts.float=false] Parse input as floats + * @param {Number} [opts.round=2] Round floats to x decimals + * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys + * @param {Function} [opts.validate] Validate function + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.error] The invalid error label + */ + + +class NumberPrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.transform = style.render(opts.style); + this.msg = opts.message; + this.initial = isDef(opts.initial) ? opts.initial : ''; + this.float = !!opts.float; + this.round = opts.round || 2; + this.inc = opts.increment || 1; + this.min = isDef(opts.min) ? opts.min : -Infinity; + this.max = isDef(opts.max) ? opts.max : Infinity; + this.errorMsg = opts.error || `Please Enter A Valid Value`; + + this.validator = opts.validate || (() => true); + + this.color = `cyan`; + this.value = ``; + this.typed = ``; + this.lastHit = 0; + this.render(); + } + + set value(v) { + if (!v && v !== 0) { + this.placeholder = true; + this.rendered = color.gray(this.transform.render(`${this.initial}`)); + this._value = ``; + } else { + this.placeholder = false; + this.rendered = this.transform.render(`${round(v, this.round)}`); + this._value = round(v, this.round); + } + + this.fire(); + } + + get value() { + return this._value; + } + + parse(x) { + return this.float ? parseFloat(x) : parseInt(x); + } + + valid(c) { + return c === `-` || c === `.` && this.float || isNumber.test(c); + } + + reset() { + this.typed = ``; + this.value = ``; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + let x = this.value; + this.value = x !== `` ? x : this.initial; + this.done = this.aborted = true; + this.error = false; + this.fire(); + this.render(); + this.out.write(`\n`); + this.close(); + } + + validate() { + var _this = this; + + return _asyncToGenerator(function* () { + let valid = yield _this.validator(_this.value); + + if (typeof valid === `string`) { + _this.errorMsg = valid; + valid = false; + } + + _this.error = !valid; + })(); + } + + submit() { + var _this2 = this; + + return _asyncToGenerator(function* () { + yield _this2.validate(); + + if (_this2.error) { + _this2.color = `red`; + + _this2.fire(); + + _this2.render(); + + return; + } + + let x = _this2.value; + _this2.value = x !== `` ? x : _this2.initial; + _this2.done = true; + _this2.aborted = false; + _this2.error = false; + + _this2.fire(); + + _this2.render(); + + _this2.out.write(`\n`); + + _this2.close(); + })(); + } + + up() { + this.typed = ``; + + if (this.value === '') { + this.value = this.min - this.inc; + } + + if (this.value >= this.max) return this.bell(); + this.value += this.inc; + this.color = `cyan`; + this.fire(); + this.render(); + } + + down() { + this.typed = ``; + + if (this.value === '') { + this.value = this.min + this.inc; + } + + if (this.value <= this.min) return this.bell(); + this.value -= this.inc; + this.color = `cyan`; + this.fire(); + this.render(); + } + + delete() { + let val = this.value.toString(); + if (val.length === 0) return this.bell(); + this.value = this.parse(val = val.slice(0, -1)) || ``; + + if (this.value !== '' && this.value < this.min) { + this.value = this.min; + } + + this.color = `cyan`; + this.fire(); + this.render(); + } + + next() { + this.value = this.initial; + this.fire(); + this.render(); + } + + _(c, key) { + if (!this.valid(c)) return this.bell(); + const now = Date.now(); + if (now - this.lastHit > 1000) this.typed = ``; // 1s elapsed + + this.typed += c; + this.lastHit = now; + this.color = `cyan`; + if (c === `.`) return this.fire(); + this.value = Math.min(this.parse(this.typed), this.max); + if (this.value > this.max) this.value = this.max; + if (this.value < this.min) this.value = this.min; + this.fire(); + this.render(); + } + + render() { + if (this.closed) return; + + if (!this.firstRender) { + if (this.outputError) this.out.write(cursor.down(lines(this.outputError, this.out.columns) - 1) + clear(this.outputError, this.out.columns)); + this.out.write(clear(this.outputText, this.out.columns)); + } + + super.render(); + this.outputError = ''; // Print prompt + + this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(this.done), !this.done || !this.done && !this.placeholder ? color[this.color]().underline(this.rendered) : this.rendered].join(` `); // Print error + + if (this.error) { + this.outputError += this.errorMsg.split(`\n`).reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore); + } + +} + +module.exports = NumberPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/prompt.js b/node_modules/prompts/dist/elements/prompt.js new file mode 100644 index 0000000..7deef04 --- /dev/null +++ b/node_modules/prompts/dist/elements/prompt.js @@ -0,0 +1,82 @@ +'use strict'; + +const readline = require('readline'); + +const _require = require('../util'), + action = _require.action; + +const EventEmitter = require('events'); + +const _require2 = require('sisteransi'), + beep = _require2.beep, + cursor = _require2.cursor; + +const color = require('kleur'); +/** + * Base prompt skeleton + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ + + +class Prompt extends EventEmitter { + constructor(opts = {}) { + super(); + this.firstRender = true; + this.in = opts.stdin || process.stdin; + this.out = opts.stdout || process.stdout; + + this.onRender = (opts.onRender || (() => void 0)).bind(this); + + const rl = readline.createInterface({ + input: this.in, + escapeCodeTimeout: 50 + }); + readline.emitKeypressEvents(this.in, rl); + if (this.in.isTTY) this.in.setRawMode(true); + const isSelect = ['SelectPrompt', 'MultiselectPrompt'].indexOf(this.constructor.name) > -1; + + const keypress = (str, key) => { + let a = action(key, isSelect); + + if (a === false) { + this._ && this._(str, key); + } else if (typeof this[a] === 'function') { + this[a](key); + } else { + this.bell(); + } + }; + + this.close = () => { + this.out.write(cursor.show); + this.in.removeListener('keypress', keypress); + if (this.in.isTTY) this.in.setRawMode(false); + rl.close(); + this.emit(this.aborted ? 'abort' : this.exited ? 'exit' : 'submit', this.value); + this.closed = true; + }; + + this.in.on('keypress', keypress); + } + + fire() { + this.emit('state', { + value: this.value, + aborted: !!this.aborted, + exited: !!this.exited + }); + } + + bell() { + this.out.write(beep); + } + + render() { + this.onRender(color); + if (this.firstRender) this.firstRender = false; + } + +} + +module.exports = Prompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/select.js b/node_modules/prompts/dist/elements/select.js new file mode 100644 index 0000000..5e27c69 --- /dev/null +++ b/node_modules/prompts/dist/elements/select.js @@ -0,0 +1,190 @@ +'use strict'; + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('../util'), + style = _require.style, + clear = _require.clear, + figures = _require.figures, + wrap = _require.wrap, + entriesToDisplay = _require.entriesToDisplay; + +const _require2 = require('sisteransi'), + cursor = _require2.cursor; +/** + * SelectPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of choice objects + * @param {String} [opts.hint] Hint to display + * @param {Number} [opts.initial] Index of default value + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {Number} [opts.optionsPerPage=10] Max options to display at once + */ + + +class SelectPrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.msg = opts.message; + this.hint = opts.hint || '- Use arrow-keys. Return to submit.'; + this.warn = opts.warn || '- This option is disabled'; + this.cursor = opts.initial || 0; + this.choices = opts.choices.map((ch, idx) => { + if (typeof ch === 'string') ch = { + title: ch, + value: idx + }; + return { + title: ch && (ch.title || ch.value || ch), + value: ch && (ch.value === undefined ? idx : ch.value), + description: ch && ch.description, + selected: ch && ch.selected, + disabled: ch && ch.disabled + }; + }); + this.optionsPerPage = opts.optionsPerPage || 10; + this.value = (this.choices[this.cursor] || {}).value; + this.clear = clear('', this.out.columns); + this.render(); + } + + moveCursor(n) { + this.cursor = n; + this.value = this.choices[n].value; + this.fire(); + } + + reset() { + this.moveCursor(0); + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + if (!this.selection.disabled) { + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } else this.bell(); + } + + first() { + this.moveCursor(0); + this.render(); + } + + last() { + this.moveCursor(this.choices.length - 1); + this.render(); + } + + up() { + if (this.cursor === 0) { + this.moveCursor(this.choices.length - 1); + } else { + this.moveCursor(this.cursor - 1); + } + + this.render(); + } + + down() { + if (this.cursor === this.choices.length - 1) { + this.moveCursor(0); + } else { + this.moveCursor(this.cursor + 1); + } + + this.render(); + } + + next() { + this.moveCursor((this.cursor + 1) % this.choices.length); + this.render(); + } + + _(c, key) { + if (c === ' ') return this.submit(); + } + + get selection() { + return this.choices[this.cursor]; + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + let _entriesToDisplay = entriesToDisplay(this.cursor, this.choices.length, this.optionsPerPage), + startIndex = _entriesToDisplay.startIndex, + endIndex = _entriesToDisplay.endIndex; // Print prompt + + + this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(false), this.done ? this.selection.title : this.selection.disabled ? color.yellow(this.warn) : color.gray(this.hint)].join(' '); // Print choices + + if (!this.done) { + this.outputText += '\n'; + + for (let i = startIndex; i < endIndex; i++) { + let title, + prefix, + desc = '', + v = this.choices[i]; // Determine whether to display "more choices" indicators + + if (i === startIndex && startIndex > 0) { + prefix = figures.arrowUp; + } else if (i === endIndex - 1 && endIndex < this.choices.length) { + prefix = figures.arrowDown; + } else { + prefix = ' '; + } + + if (v.disabled) { + title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); + prefix = (this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' ') + prefix; + } else { + title = this.cursor === i ? color.cyan().underline(v.title) : v.title; + prefix = (this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; + + if (v.description && this.cursor === i) { + desc = ` - ${v.description}`; + + if (prefix.length + title.length + desc.length >= this.out.columns || v.description.split(/\r?\n/).length > 1) { + desc = '\n' + wrap(v.description, { + margin: 3, + width: this.out.columns + }); + } + } + } + + this.outputText += `${prefix} ${title}${color.gray(desc)}\n`; + } + } + + this.out.write(this.outputText); + } + +} + +module.exports = SelectPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/text.js b/node_modules/prompts/dist/elements/text.js new file mode 100644 index 0000000..c38a070 --- /dev/null +++ b/node_modules/prompts/dist/elements/text.js @@ -0,0 +1,245 @@ +"use strict"; + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('sisteransi'), + erase = _require.erase, + cursor = _require.cursor; + +const _require2 = require('../util'), + style = _require2.style, + clear = _require2.clear, + lines = _require2.lines, + figures = _require2.figures; +/** + * TextPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {String} [opts.style='default'] Render style + * @param {String} [opts.initial] Default value + * @param {Function} [opts.validate] Validate function + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.error] The invalid error label + */ + + +class TextPrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.transform = style.render(opts.style); + this.scale = this.transform.scale; + this.msg = opts.message; + this.initial = opts.initial || ``; + + this.validator = opts.validate || (() => true); + + this.value = ``; + this.errorMsg = opts.error || `Please Enter A Valid Value`; + this.cursor = Number(!!this.initial); + this.cursorOffset = 0; + this.clear = clear(``, this.out.columns); + this.render(); + } + + set value(v) { + if (!v && this.initial) { + this.placeholder = true; + this.rendered = color.gray(this.transform.render(this.initial)); + } else { + this.placeholder = false; + this.rendered = this.transform.render(v); + } + + this._value = v; + this.fire(); + } + + get value() { + return this._value; + } + + reset() { + this.value = ``; + this.cursor = Number(!!this.initial); + this.cursorOffset = 0; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.value = this.value || this.initial; + this.done = this.aborted = true; + this.error = false; + this.red = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + validate() { + var _this = this; + + return _asyncToGenerator(function* () { + let valid = yield _this.validator(_this.value); + + if (typeof valid === `string`) { + _this.errorMsg = valid; + valid = false; + } + + _this.error = !valid; + })(); + } + + submit() { + var _this2 = this; + + return _asyncToGenerator(function* () { + _this2.value = _this2.value || _this2.initial; + _this2.cursorOffset = 0; + _this2.cursor = _this2.rendered.length; + yield _this2.validate(); + + if (_this2.error) { + _this2.red = true; + + _this2.fire(); + + _this2.render(); + + return; + } + + _this2.done = true; + _this2.aborted = false; + + _this2.fire(); + + _this2.render(); + + _this2.out.write('\n'); + + _this2.close(); + })(); + } + + next() { + if (!this.placeholder) return this.bell(); + this.value = this.initial; + this.cursor = this.rendered.length; + this.fire(); + this.render(); + } + + moveCursor(n) { + if (this.placeholder) return; + this.cursor = this.cursor + n; + this.cursorOffset += n; + } + + _(c, key) { + let s1 = this.value.slice(0, this.cursor); + let s2 = this.value.slice(this.cursor); + this.value = `${s1}${c}${s2}`; + this.red = false; + this.cursor = this.placeholder ? 0 : s1.length + 1; + this.render(); + } + + delete() { + if (this.isCursorAtStart()) return this.bell(); + let s1 = this.value.slice(0, this.cursor - 1); + let s2 = this.value.slice(this.cursor); + this.value = `${s1}${s2}`; + this.red = false; + + if (this.isCursorAtStart()) { + this.cursorOffset = 0; + } else { + this.cursorOffset++; + this.moveCursor(-1); + } + + this.render(); + } + + deleteForward() { + if (this.cursor * this.scale >= this.rendered.length || this.placeholder) return this.bell(); + let s1 = this.value.slice(0, this.cursor); + let s2 = this.value.slice(this.cursor + 1); + this.value = `${s1}${s2}`; + this.red = false; + + if (this.isCursorAtEnd()) { + this.cursorOffset = 0; + } else { + this.cursorOffset++; + } + + this.render(); + } + + first() { + this.cursor = 0; + this.render(); + } + + last() { + this.cursor = this.value.length; + this.render(); + } + + left() { + if (this.cursor <= 0 || this.placeholder) return this.bell(); + this.moveCursor(-1); + this.render(); + } + + right() { + if (this.cursor * this.scale >= this.rendered.length || this.placeholder) return this.bell(); + this.moveCursor(1); + this.render(); + } + + isCursorAtStart() { + return this.cursor === 0 || this.placeholder && this.cursor === 1; + } + + isCursorAtEnd() { + return this.cursor === this.rendered.length || this.placeholder && this.cursor === this.rendered.length + 1; + } + + render() { + if (this.closed) return; + + if (!this.firstRender) { + if (this.outputError) this.out.write(cursor.down(lines(this.outputError, this.out.columns) - 1) + clear(this.outputError, this.out.columns)); + this.out.write(clear(this.outputText, this.out.columns)); + } + + super.render(); + this.outputError = ''; + this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(this.done), this.red ? color.red(this.rendered) : this.rendered].join(` `); + + if (this.error) { + this.outputError += this.errorMsg.split(`\n`).reduce((a, l, i) => a + `\n${i ? ' ' : figures.pointerSmall} ${color.red().italic(l)}`, ``); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore + cursor.move(this.cursorOffset, 0)); + } + +} + +module.exports = TextPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/elements/toggle.js b/node_modules/prompts/dist/elements/toggle.js new file mode 100644 index 0000000..d942f60 --- /dev/null +++ b/node_modules/prompts/dist/elements/toggle.js @@ -0,0 +1,124 @@ +"use strict"; + +const color = require('kleur'); + +const Prompt = require('./prompt'); + +const _require = require('../util'), + style = _require.style, + clear = _require.clear; + +const _require2 = require('sisteransi'), + cursor = _require2.cursor, + erase = _require2.erase; +/** + * TogglePrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Boolean} [opts.initial=false] Default value + * @param {String} [opts.active='no'] Active label + * @param {String} [opts.inactive='off'] Inactive label + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ + + +class TogglePrompt extends Prompt { + constructor(opts = {}) { + super(opts); + this.msg = opts.message; + this.value = !!opts.initial; + this.active = opts.active || 'on'; + this.inactive = opts.inactive || 'off'; + this.initialValue = this.value; + this.render(); + } + + reset() { + this.value = this.initialValue; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + deactivate() { + if (this.value === false) return this.bell(); + this.value = false; + this.render(); + } + + activate() { + if (this.value === true) return this.bell(); + this.value = true; + this.render(); + } + + delete() { + this.deactivate(); + } + + left() { + this.deactivate(); + } + + right() { + this.activate(); + } + + down() { + this.deactivate(); + } + + up() { + this.activate(); + } + + next() { + this.value = !this.value; + this.fire(); + this.render(); + } + + _(c, key) { + if (c === ' ') { + this.value = !this.value; + } else if (c === '1') { + this.value = true; + } else if (c === '0') { + this.value = false; + } else return this.bell(); + + this.render(); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(this.done), this.value ? this.inactive : color.cyan().underline(this.inactive), color.gray('/'), this.value ? color.cyan().underline(this.active) : this.active].join(' '); + this.out.write(erase.line + cursor.to(0) + this.outputText); + } + +} + +module.exports = TogglePrompt;
\ No newline at end of file diff --git a/node_modules/prompts/dist/index.js b/node_modules/prompts/dist/index.js new file mode 100644 index 0000000..f30e9e7 --- /dev/null +++ b/node_modules/prompts/dist/index.js @@ -0,0 +1,154 @@ +'use strict'; + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } + +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } + +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +const prompts = require('./prompts'); + +const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender', 'type']; + +const noop = () => {}; +/** + * Prompt for a series of questions + * @param {Array|Object} questions Single question object or Array of question objects + * @param {Function} [onSubmit] Callback function called on prompt submit + * @param {Function} [onCancel] Callback function called on cancel/abort + * @returns {Object} Object with values from user input + */ + + +function prompt() { + return _prompt.apply(this, arguments); +} + +function _prompt() { + _prompt = _asyncToGenerator(function* (questions = [], { + onSubmit = noop, + onCancel = noop + } = {}) { + const answers = {}; + const override = prompt._override || {}; + questions = [].concat(questions); + let answer, question, quit, name, type, lastPrompt; + + const getFormattedAnswer = /*#__PURE__*/function () { + var _ref = _asyncToGenerator(function* (question, answer, skipValidation = false) { + if (!skipValidation && question.validate && question.validate(answer) !== true) { + return; + } + + return question.format ? yield question.format(answer, answers) : answer; + }); + + return function getFormattedAnswer(_x, _x2) { + return _ref.apply(this, arguments); + }; + }(); + + var _iterator = _createForOfIteratorHelper(questions), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + question = _step.value; + var _question = question; + name = _question.name; + type = _question.type; + + // evaluate type first and skip if type is a falsy value + if (typeof type === 'function') { + type = yield type(answer, _objectSpread({}, answers), question); + question['type'] = type; + } + + if (!type) continue; // if property is a function, invoke it unless it's a special function + + for (let key in question) { + if (passOn.includes(key)) continue; + let value = question[key]; + question[key] = typeof value === 'function' ? yield value(answer, _objectSpread({}, answers), lastPrompt) : value; + } + + lastPrompt = question; + + if (typeof question.message !== 'string') { + throw new Error('prompt message is required'); + } // update vars in case they changed + + + var _question2 = question; + name = _question2.name; + type = _question2.type; + + if (prompts[type] === void 0) { + throw new Error(`prompt type (${type}) is not defined`); + } + + if (override[question.name] !== undefined) { + answer = yield getFormattedAnswer(question, override[question.name]); + + if (answer !== undefined) { + answers[name] = answer; + continue; + } + } + + try { + // Get the injected answer if there is one or prompt the user + answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : yield prompts[type](question); + answers[name] = answer = yield getFormattedAnswer(question, answer, true); + quit = yield onSubmit(question, answer, answers); + } catch (err) { + quit = !(yield onCancel(question, answers)); + } + + if (quit) return answers; + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + + return answers; + }); + return _prompt.apply(this, arguments); +} + +function getInjectedAnswer(injected, deafultValue) { + const answer = injected.shift(); + + if (answer instanceof Error) { + throw answer; + } + + return answer === undefined ? deafultValue : answer; +} + +function inject(answers) { + prompt._injected = (prompt._injected || []).concat(answers); +} + +function override(answers) { + prompt._override = Object.assign({}, answers); +} + +module.exports = Object.assign(prompt, { + prompt, + prompts, + inject, + override +});
\ No newline at end of file diff --git a/node_modules/prompts/dist/prompts.js b/node_modules/prompts/dist/prompts.js new file mode 100644 index 0000000..31f2648 --- /dev/null +++ b/node_modules/prompts/dist/prompts.js @@ -0,0 +1,222 @@ +'use strict'; + +const $ = exports; + +const el = require('./elements'); + +const noop = v => v; + +function toPrompt(type, args, opts = {}) { + return new Promise((res, rej) => { + const p = new el[type](args); + const onAbort = opts.onAbort || noop; + const onSubmit = opts.onSubmit || noop; + const onExit = opts.onExit || noop; + p.on('state', args.onState || noop); + p.on('submit', x => res(onSubmit(x))); + p.on('exit', x => res(onExit(x))); + p.on('abort', x => rej(onAbort(x))); + }); +} +/** + * Text prompt + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {function} [args.onState] On state change callback + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.text = args => toPrompt('TextPrompt', args); +/** + * Password prompt with masked input + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {function} [args.onState] On state change callback + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.password = args => { + args.style = 'password'; + return $.text(args); +}; +/** + * Prompt where input is invisible, like sudo + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {function} [args.onState] On state change callback + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.invisible = args => { + args.style = 'invisible'; + return $.text(args); +}; +/** + * Number prompt + * @param {string} args.message Prompt message to display + * @param {number} args.initial Default number value + * @param {function} [args.onState] On state change callback + * @param {number} [args.max] Max value + * @param {number} [args.min] Min value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {Boolean} [opts.float=false] Parse input as floats + * @param {Number} [opts.round=2] Round floats to x decimals + * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.number = args => toPrompt('NumberPrompt', args); +/** + * Date prompt + * @param {string} args.message Prompt message to display + * @param {number} args.initial Default number value + * @param {function} [args.onState] On state change callback + * @param {number} [args.max] Max value + * @param {number} [args.min] Min value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {Boolean} [opts.float=false] Parse input as floats + * @param {Number} [opts.round=2] Round floats to x decimals + * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.date = args => toPrompt('DatePrompt', args); +/** + * Classic yes/no prompt + * @param {string} args.message Prompt message to display + * @param {boolean} [args.initial=false] Default value + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.confirm = args => toPrompt('ConfirmPrompt', args); +/** + * List prompt, split intput string by `seperator` + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {string} [args.separator] String separator + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input, in form of an `Array` + */ + + +$.list = args => { + const sep = args.separator || ','; + return toPrompt('TextPrompt', args, { + onSubmit: str => str.split(sep).map(s => s.trim()) + }); +}; +/** + * Toggle/switch prompt + * @param {string} args.message Prompt message to display + * @param {boolean} [args.initial=false] Default value + * @param {string} [args.active="on"] Text for `active` state + * @param {string} [args.inactive="off"] Text for `inactive` state + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.toggle = args => toPrompt('TogglePrompt', args); +/** + * Interactive select prompt + * @param {string} args.message Prompt message to display + * @param {Array} args.choices Array of choices objects `[{ title, value }, ...]` + * @param {number} [args.initial] Index of default value + * @param {String} [args.hint] Hint to display + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.select = args => toPrompt('SelectPrompt', args); +/** + * Interactive multi-select / autocompleteMultiselect prompt + * @param {string} args.message Prompt message to display + * @param {Array} args.choices Array of choices objects `[{ title, value, [selected] }, ...]` + * @param {number} [args.max] Max select + * @param {string} [args.hint] Hint to display user + * @param {Number} [args.cursor=0] Cursor start position + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.multiselect = args => { + args.choices = [].concat(args.choices || []); + + const toSelected = items => items.filter(item => item.selected).map(item => item.value); + + return toPrompt('MultiselectPrompt', args, { + onAbort: toSelected, + onSubmit: toSelected + }); +}; + +$.autocompleteMultiselect = args => { + args.choices = [].concat(args.choices || []); + + const toSelected = items => items.filter(item => item.selected).map(item => item.value); + + return toPrompt('AutocompleteMultiselectPrompt', args, { + onAbort: toSelected, + onSubmit: toSelected + }); +}; + +const byTitle = (input, choices) => Promise.resolve(choices.filter(item => item.title.slice(0, input.length).toLowerCase() === input.toLowerCase())); +/** + * Interactive auto-complete prompt + * @param {string} args.message Prompt message to display + * @param {Array} args.choices Array of auto-complete choices objects `[{ title, value }, ...]` + * @param {Function} [args.suggest] Function to filter results based on user input. Defaults to sort by `title` + * @param {number} [args.limit=10] Max number of results to show + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {String} [args.initial] Index of the default value + * @param {boolean} [opts.clearFirst] The first ESCAPE keypress will clear the input + * @param {String} [args.fallback] Fallback message - defaults to initial value + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ + + +$.autocomplete = args => { + args.suggest = args.suggest || byTitle; + args.choices = [].concat(args.choices || []); + return toPrompt('AutocompletePrompt', args); +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/action.js b/node_modules/prompts/dist/util/action.js new file mode 100644 index 0000000..c36b7db --- /dev/null +++ b/node_modules/prompts/dist/util/action.js @@ -0,0 +1,38 @@ +'use strict'; + +module.exports = (key, isSelect) => { + if (key.meta && key.name !== 'escape') return; + + if (key.ctrl) { + if (key.name === 'a') return 'first'; + if (key.name === 'c') return 'abort'; + if (key.name === 'd') return 'abort'; + if (key.name === 'e') return 'last'; + if (key.name === 'g') return 'reset'; + } + + if (isSelect) { + if (key.name === 'j') return 'down'; + if (key.name === 'k') return 'up'; + } + + if (key.name === 'return') return 'submit'; + if (key.name === 'enter') return 'submit'; // ctrl + J + + if (key.name === 'backspace') return 'delete'; + if (key.name === 'delete') return 'deleteForward'; + if (key.name === 'abort') return 'abort'; + if (key.name === 'escape') return 'exit'; + if (key.name === 'tab') return 'next'; + if (key.name === 'pagedown') return 'nextPage'; + if (key.name === 'pageup') return 'prevPage'; // TODO create home() in prompt types (e.g. TextPrompt) + + if (key.name === 'home') return 'home'; // TODO create end() in prompt types (e.g. TextPrompt) + + if (key.name === 'end') return 'end'; + if (key.name === 'up') return 'up'; + if (key.name === 'down') return 'down'; + if (key.name === 'right') return 'right'; + if (key.name === 'left') return 'left'; + return false; +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/clear.js b/node_modules/prompts/dist/util/clear.js new file mode 100644 index 0000000..5a313be --- /dev/null +++ b/node_modules/prompts/dist/util/clear.js @@ -0,0 +1,42 @@ +'use strict'; + +function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } + +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } + +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + +const strip = require('./strip'); + +const _require = require('sisteransi'), + erase = _require.erase, + cursor = _require.cursor; + +const width = str => [...strip(str)].length; +/** + * @param {string} prompt + * @param {number} perLine + */ + + +module.exports = function (prompt, perLine) { + if (!perLine) return erase.line + cursor.to(0); + let rows = 0; + const lines = prompt.split(/\r?\n/); + + var _iterator = _createForOfIteratorHelper(lines), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + let line = _step.value; + rows += 1 + Math.floor(Math.max(width(line) - 1, 0) / perLine); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + + return erase.lines(rows); +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/entriesToDisplay.js b/node_modules/prompts/dist/util/entriesToDisplay.js new file mode 100644 index 0000000..fbb963a --- /dev/null +++ b/node_modules/prompts/dist/util/entriesToDisplay.js @@ -0,0 +1,21 @@ +'use strict'; +/** + * Determine what entries should be displayed on the screen, based on the + * currently selected index and the maximum visible. Used in list-based + * prompts like `select` and `multiselect`. + * + * @param {number} cursor the currently selected entry + * @param {number} total the total entries available to display + * @param {number} [maxVisible] the number of entries that can be displayed + */ + +module.exports = (cursor, total, maxVisible) => { + maxVisible = maxVisible || total; + let startIndex = Math.min(total - maxVisible, cursor - Math.floor(maxVisible / 2)); + if (startIndex < 0) startIndex = 0; + let endIndex = Math.min(startIndex + maxVisible, total); + return { + startIndex, + endIndex + }; +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/figures.js b/node_modules/prompts/dist/util/figures.js new file mode 100644 index 0000000..036ec50 --- /dev/null +++ b/node_modules/prompts/dist/util/figures.js @@ -0,0 +1,32 @@ +'use strict'; + +const main = { + arrowUp: '↑', + arrowDown: '↓', + arrowLeft: '←', + arrowRight: '→', + radioOn: '◉', + radioOff: '◯', + tick: '✔', + cross: '✖', + ellipsis: '…', + pointerSmall: '›', + line: '─', + pointer: '❯' +}; +const win = { + arrowUp: main.arrowUp, + arrowDown: main.arrowDown, + arrowLeft: main.arrowLeft, + arrowRight: main.arrowRight, + radioOn: '(*)', + radioOff: '( )', + tick: '√', + cross: '×', + ellipsis: '...', + pointerSmall: '»', + line: '─', + pointer: '>' +}; +const figures = process.platform === 'win32' ? win : main; +module.exports = figures;
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/index.js b/node_modules/prompts/dist/util/index.js new file mode 100644 index 0000000..dbfe75e --- /dev/null +++ b/node_modules/prompts/dist/util/index.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = { + action: require('./action'), + clear: require('./clear'), + style: require('./style'), + strip: require('./strip'), + figures: require('./figures'), + lines: require('./lines'), + wrap: require('./wrap'), + entriesToDisplay: require('./entriesToDisplay') +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/lines.js b/node_modules/prompts/dist/util/lines.js new file mode 100644 index 0000000..54a939a --- /dev/null +++ b/node_modules/prompts/dist/util/lines.js @@ -0,0 +1,14 @@ +'use strict'; + +const strip = require('./strip'); +/** + * @param {string} msg + * @param {number} perLine + */ + + +module.exports = function (msg, perLine) { + let lines = String(strip(msg) || '').split(/\r?\n/); + if (!perLine) return lines.length; + return lines.map(l => Math.ceil(l.length / perLine)).reduce((a, b) => a + b); +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/strip.js b/node_modules/prompts/dist/util/strip.js new file mode 100644 index 0000000..dd289a0 --- /dev/null +++ b/node_modules/prompts/dist/util/strip.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = str => { + const pattern = ['[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))'].join('|'); + const RGX = new RegExp(pattern, 'g'); + return typeof str === 'string' ? str.replace(RGX, '') : str; +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/style.js b/node_modules/prompts/dist/util/style.js new file mode 100644 index 0000000..ed1a49b --- /dev/null +++ b/node_modules/prompts/dist/util/style.js @@ -0,0 +1,51 @@ +'use strict'; + +const c = require('kleur'); + +const figures = require('./figures'); // rendering user input. + + +const styles = Object.freeze({ + password: { + scale: 1, + render: input => '*'.repeat(input.length) + }, + emoji: { + scale: 2, + render: input => '😃'.repeat(input.length) + }, + invisible: { + scale: 0, + render: input => '' + }, + default: { + scale: 1, + render: input => `${input}` + } +}); + +const render = type => styles[type] || styles.default; // icon to signalize a prompt. + + +const symbols = Object.freeze({ + aborted: c.red(figures.cross), + done: c.green(figures.tick), + exited: c.yellow(figures.cross), + default: c.cyan('?') +}); + +const symbol = (done, aborted, exited) => aborted ? symbols.aborted : exited ? symbols.exited : done ? symbols.done : symbols.default; // between the question and the user's input. + + +const delimiter = completing => c.gray(completing ? figures.ellipsis : figures.pointerSmall); + +const item = (expandable, expanded) => c.gray(expandable ? expanded ? figures.pointerSmall : '+' : figures.line); + +module.exports = { + styles, + render, + symbols, + symbol, + delimiter, + item +};
\ No newline at end of file diff --git a/node_modules/prompts/dist/util/wrap.js b/node_modules/prompts/dist/util/wrap.js new file mode 100644 index 0000000..da5e67c --- /dev/null +++ b/node_modules/prompts/dist/util/wrap.js @@ -0,0 +1,16 @@ +'use strict'; +/** + * @param {string} msg The message to wrap + * @param {object} opts + * @param {number|string} [opts.margin] Left margin + * @param {number} opts.width Maximum characters per line including the margin + */ + +module.exports = (msg, opts = {}) => { + const tab = Number.isSafeInteger(parseInt(opts.margin)) ? new Array(parseInt(opts.margin)).fill(' ').join('') : opts.margin || ''; + const width = opts.width; + return (msg || '').split(/\r?\n/g).map(line => line.split(/\s+/g).reduce((arr, w) => { + if (w.length + tab.length >= width || arr[arr.length - 1].length + w.length + 1 < width) arr[arr.length - 1] += ` ${w}`;else arr.push(`${tab}${w}`); + return arr; + }, [tab]).join('\n')).join('\n'); +};
\ No newline at end of file diff --git a/node_modules/prompts/index.js b/node_modules/prompts/index.js new file mode 100644 index 0000000..3479956 --- /dev/null +++ b/node_modules/prompts/index.js @@ -0,0 +1,14 @@ +function isNodeLT(tar) { + tar = (Array.isArray(tar) ? tar : tar.split('.')).map(Number); + let i=0, src=process.versions.node.split('.').map(Number); + for (; i < tar.length; i++) { + if (src[i] > tar[i]) return false; + if (tar[i] > src[i]) return true; + } + return false; +} + +module.exports = + isNodeLT('8.6.0') + ? require('./dist/index.js') + : require('./lib/index.js'); diff --git a/node_modules/prompts/lib/dateparts/datepart.js b/node_modules/prompts/lib/dateparts/datepart.js new file mode 100644 index 0000000..62b893b --- /dev/null +++ b/node_modules/prompts/lib/dateparts/datepart.js @@ -0,0 +1,35 @@ +'use strict'; + +class DatePart { + constructor({token, date, parts, locales}) { + this.token = token; + this.date = date || new Date(); + this.parts = parts || [this]; + this.locales = locales || {}; + } + + up() {} + + down() {} + + next() { + const currentIdx = this.parts.indexOf(this); + return this.parts.find((part, idx) => idx > currentIdx && part instanceof DatePart); + } + + setTo(val) {} + + prev() { + let parts = [].concat(this.parts).reverse(); + const currentIdx = parts.indexOf(this); + return parts.find((part, idx) => idx > currentIdx && part instanceof DatePart); + } + + toString() { + return String(this.date); + } +} + +module.exports = DatePart; + + diff --git a/node_modules/prompts/lib/dateparts/day.js b/node_modules/prompts/lib/dateparts/day.js new file mode 100644 index 0000000..5db84fe --- /dev/null +++ b/node_modules/prompts/lib/dateparts/day.js @@ -0,0 +1,42 @@ +'use strict'; + +const DatePart = require('./datepart'); + +const pos = n => { + n = n % 10; + return n === 1 ? 'st' + : n === 2 ? 'nd' + : n === 3 ? 'rd' + : 'th'; +} + +class Day extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setDate(this.date.getDate() + 1); + } + + down() { + this.date.setDate(this.date.getDate() - 1); + } + + setTo(val) { + this.date.setDate(parseInt(val.substr(-2))); + } + + toString() { + let date = this.date.getDate(); + let day = this.date.getDay(); + return this.token === 'DD' ? String(date).padStart(2, '0') + : this.token === 'Do' ? date + pos(date) + : this.token === 'd' ? day + 1 + : this.token === 'ddd' ? this.locales.weekdaysShort[day] + : this.token === 'dddd' ? this.locales.weekdays[day] + : date; + } +} + +module.exports = Day; diff --git a/node_modules/prompts/lib/dateparts/hours.js b/node_modules/prompts/lib/dateparts/hours.js new file mode 100644 index 0000000..171b3d2 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/hours.js @@ -0,0 +1,30 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Hours extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setHours(this.date.getHours() + 1); + } + + down() { + this.date.setHours(this.date.getHours() - 1); + } + + setTo(val) { + this.date.setHours(parseInt(val.substr(-2))); + } + + toString() { + let hours = this.date.getHours(); + if (/h/.test(this.token)) + hours = (hours % 12) || 12; + return this.token.length > 1 ? String(hours).padStart(2, '0') : hours; + } +} + +module.exports = Hours; diff --git a/node_modules/prompts/lib/dateparts/index.js b/node_modules/prompts/lib/dateparts/index.js new file mode 100644 index 0000000..dc0cc95 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/index.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + DatePart: require('./datepart'), + Meridiem: require('./meridiem'), + Day: require('./day'), + Hours: require('./hours'), + Milliseconds: require('./milliseconds'), + Minutes: require('./minutes'), + Month: require('./month'), + Seconds: require('./seconds'), + Year: require('./year'), +} diff --git a/node_modules/prompts/lib/dateparts/meridiem.js b/node_modules/prompts/lib/dateparts/meridiem.js new file mode 100644 index 0000000..8488677 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/meridiem.js @@ -0,0 +1,24 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Meridiem extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setHours((this.date.getHours() + 12) % 24); + } + + down() { + this.up(); + } + + toString() { + let meridiem = this.date.getHours() > 12 ? 'pm' : 'am'; + return /\A/.test(this.token) ? meridiem.toUpperCase() : meridiem; + } +} + +module.exports = Meridiem; diff --git a/node_modules/prompts/lib/dateparts/milliseconds.js b/node_modules/prompts/lib/dateparts/milliseconds.js new file mode 100644 index 0000000..8984270 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/milliseconds.js @@ -0,0 +1,28 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Milliseconds extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setMilliseconds(this.date.getMilliseconds() + 1); + } + + down() { + this.date.setMilliseconds(this.date.getMilliseconds() - 1); + } + + setTo(val) { + this.date.setMilliseconds(parseInt(val.substr(-(this.token.length)))); + } + + toString() { + return String(this.date.getMilliseconds()).padStart(4, '0') + .substr(0, this.token.length); + } +} + +module.exports = Milliseconds; diff --git a/node_modules/prompts/lib/dateparts/minutes.js b/node_modules/prompts/lib/dateparts/minutes.js new file mode 100644 index 0000000..aa1d8f7 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/minutes.js @@ -0,0 +1,28 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Minutes extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setMinutes(this.date.getMinutes() + 1); + } + + down() { + this.date.setMinutes(this.date.getMinutes() - 1); + } + + setTo(val) { + this.date.setMinutes(parseInt(val.substr(-2))); + } + + toString() { + let m = this.date.getMinutes(); + return this.token.length > 1 ? String(m).padStart(2, '0') : m; + } +} + +module.exports = Minutes; diff --git a/node_modules/prompts/lib/dateparts/month.js b/node_modules/prompts/lib/dateparts/month.js new file mode 100644 index 0000000..f656455 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/month.js @@ -0,0 +1,33 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Month extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setMonth(this.date.getMonth() + 1); + } + + down() { + this.date.setMonth(this.date.getMonth() - 1); + } + + setTo(val) { + val = parseInt(val.substr(-2)) - 1; + this.date.setMonth(val < 0 ? 0 : val); + } + + toString() { + let month = this.date.getMonth(); + let tl = this.token.length; + return tl === 2 ? String(month + 1).padStart(2, '0') + : tl === 3 ? this.locales.monthsShort[month] + : tl === 4 ? this.locales.months[month] + : String(month + 1); + } +} + +module.exports = Month; diff --git a/node_modules/prompts/lib/dateparts/seconds.js b/node_modules/prompts/lib/dateparts/seconds.js new file mode 100644 index 0000000..0c1a1a4 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/seconds.js @@ -0,0 +1,28 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Seconds extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setSeconds(this.date.getSeconds() + 1); + } + + down() { + this.date.setSeconds(this.date.getSeconds() - 1); + } + + setTo(val) { + this.date.setSeconds(parseInt(val.substr(-2))); + } + + toString() { + let s = this.date.getSeconds(); + return this.token.length > 1 ? String(s).padStart(2, '0') : s; + } +} + +module.exports = Seconds; diff --git a/node_modules/prompts/lib/dateparts/year.js b/node_modules/prompts/lib/dateparts/year.js new file mode 100644 index 0000000..f068e43 --- /dev/null +++ b/node_modules/prompts/lib/dateparts/year.js @@ -0,0 +1,28 @@ +'use strict'; + +const DatePart = require('./datepart'); + +class Year extends DatePart { + constructor(opts={}) { + super(opts); + } + + up() { + this.date.setFullYear(this.date.getFullYear() + 1); + } + + down() { + this.date.setFullYear(this.date.getFullYear() - 1); + } + + setTo(val) { + this.date.setFullYear(val.substr(-4)); + } + + toString() { + let year = String(this.date.getFullYear()).padStart(4, '0'); + return this.token.length === 2 ? year.substr(-2) : year; + } +} + +module.exports = Year; diff --git a/node_modules/prompts/lib/elements/autocomplete.js b/node_modules/prompts/lib/elements/autocomplete.js new file mode 100644 index 0000000..8e06da2 --- /dev/null +++ b/node_modules/prompts/lib/elements/autocomplete.js @@ -0,0 +1,264 @@ +'use strict'; + +const color = require('kleur'); +const Prompt = require('./prompt'); +const { erase, cursor } = require('sisteransi'); +const { style, clear, figures, wrap, entriesToDisplay } = require('../util'); + +const getVal = (arr, i) => arr[i] && (arr[i].value || arr[i].title || arr[i]); +const getTitle = (arr, i) => arr[i] && (arr[i].title || arr[i].value || arr[i]); +const getIndex = (arr, valOrTitle) => { + const index = arr.findIndex(el => el.value === valOrTitle || el.title === valOrTitle); + return index > -1 ? index : undefined; +}; + +/** + * TextPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of auto-complete choices objects + * @param {Function} [opts.suggest] Filter function. Defaults to sort by title + * @param {Number} [opts.limit=10] Max number of results to show + * @param {Number} [opts.cursor=0] Cursor start position + * @param {String} [opts.style='default'] Render style + * @param {String} [opts.fallback] Fallback message - initial to default value + * @param {String} [opts.initial] Index of the default value + * @param {Boolean} [opts.clearFirst] The first ESCAPE keypress will clear the input + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.noMatches] The no matches found label + */ +class AutocompletePrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.msg = opts.message; + this.suggest = opts.suggest; + this.choices = opts.choices; + this.initial = typeof opts.initial === 'number' + ? opts.initial + : getIndex(opts.choices, opts.initial); + this.select = this.initial || opts.cursor || 0; + this.i18n = { noMatches: opts.noMatches || 'no matches found' }; + this.fallback = opts.fallback || this.initial; + this.clearFirst = opts.clearFirst || false; + this.suggestions = []; + this.input = ''; + this.limit = opts.limit || 10; + this.cursor = 0; + this.transform = style.render(opts.style); + this.scale = this.transform.scale; + this.render = this.render.bind(this); + this.complete = this.complete.bind(this); + this.clear = clear('', this.out.columns); + this.complete(this.render); + this.render(); + } + + set fallback(fb) { + this._fb = Number.isSafeInteger(parseInt(fb)) ? parseInt(fb) : fb; + } + + get fallback() { + let choice; + if (typeof this._fb === 'number') + choice = this.choices[this._fb]; + else if (typeof this._fb === 'string') + choice = { title: this._fb }; + return choice || this._fb || { title: this.i18n.noMatches }; + } + + moveSelect(i) { + this.select = i; + if (this.suggestions.length > 0) + this.value = getVal(this.suggestions, i); + else this.value = this.fallback.value; + this.fire(); + } + + async complete(cb) { + const p = (this.completing = this.suggest(this.input, this.choices)); + const suggestions = await p; + + if (this.completing !== p) return; + this.suggestions = suggestions + .map((s, i, arr) => ({ title: getTitle(arr, i), value: getVal(arr, i), description: s.description })); + this.completing = false; + const l = Math.max(suggestions.length - 1, 0); + this.moveSelect(Math.min(l, this.select)); + + cb && cb(); + } + + reset() { + this.input = ''; + this.complete(() => { + this.moveSelect(this.initial !== void 0 ? this.initial : 0); + this.render(); + }); + this.render(); + } + + exit() { + if (this.clearFirst && this.input.length > 0) { + this.reset(); + } else { + this.done = this.exited = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + } + + abort() { + this.done = this.aborted = true; + this.exited = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + this.done = true; + this.aborted = this.exited = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + _(c, key) { + let s1 = this.input.slice(0, this.cursor); + let s2 = this.input.slice(this.cursor); + this.input = `${s1}${c}${s2}`; + this.cursor = s1.length+1; + this.complete(this.render); + this.render(); + } + + delete() { + if (this.cursor === 0) return this.bell(); + let s1 = this.input.slice(0, this.cursor-1); + let s2 = this.input.slice(this.cursor); + this.input = `${s1}${s2}`; + this.complete(this.render); + this.cursor = this.cursor-1; + this.render(); + } + + deleteForward() { + if(this.cursor*this.scale >= this.rendered.length) return this.bell(); + let s1 = this.input.slice(0, this.cursor); + let s2 = this.input.slice(this.cursor+1); + this.input = `${s1}${s2}`; + this.complete(this.render); + this.render(); + } + + first() { + this.moveSelect(0); + this.render(); + } + + last() { + this.moveSelect(this.suggestions.length - 1); + this.render(); + } + + up() { + if (this.select === 0) { + this.moveSelect(this.suggestions.length - 1); + } else { + this.moveSelect(this.select - 1); + } + this.render(); + } + + down() { + if (this.select === this.suggestions.length - 1) { + this.moveSelect(0); + } else { + this.moveSelect(this.select + 1); + } + this.render(); + } + + next() { + if (this.select === this.suggestions.length - 1) { + this.moveSelect(0); + } else this.moveSelect(this.select + 1); + this.render(); + } + + nextPage() { + this.moveSelect(Math.min(this.select + this.limit, this.suggestions.length - 1)); + this.render(); + } + + prevPage() { + this.moveSelect(Math.max(this.select - this.limit, 0)); + this.render(); + } + + left() { + if (this.cursor <= 0) return this.bell(); + this.cursor = this.cursor-1; + this.render(); + } + + right() { + if (this.cursor*this.scale >= this.rendered.length) return this.bell(); + this.cursor = this.cursor+1; + this.render(); + } + + renderOption(v, hovered, isStart, isEnd) { + let desc; + let prefix = isStart ? figures.arrowUp : isEnd ? figures.arrowDown : ' '; + let title = hovered ? color.cyan().underline(v.title) : v.title; + prefix = (hovered ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; + if (v.description) { + desc = ` - ${v.description}`; + if (prefix.length + title.length + desc.length >= this.out.columns + || v.description.split(/\r?\n/).length > 1) { + desc = '\n' + wrap(v.description, { margin: 3, width: this.out.columns }) + } + } + return prefix + ' ' + title + color.gray(desc || ''); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + let { startIndex, endIndex } = entriesToDisplay(this.select, this.choices.length, this.limit); + + this.outputText = [ + style.symbol(this.done, this.aborted, this.exited), + color.bold(this.msg), + style.delimiter(this.completing), + this.done && this.suggestions[this.select] + ? this.suggestions[this.select].title + : this.rendered = this.transform.render(this.input) + ].join(' '); + + if (!this.done) { + const suggestions = this.suggestions + .slice(startIndex, endIndex) + .map((item, i) => this.renderOption(item, + this.select === i + startIndex, + i === 0 && startIndex > 0, + i + startIndex === endIndex - 1 && endIndex < this.choices.length)) + .join('\n'); + this.outputText += `\n` + (suggestions || color.gray(this.fallback.title)); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText); + } +} + +module.exports = AutocompletePrompt; diff --git a/node_modules/prompts/lib/elements/autocompleteMultiselect.js b/node_modules/prompts/lib/elements/autocompleteMultiselect.js new file mode 100644 index 0000000..b64510f --- /dev/null +++ b/node_modules/prompts/lib/elements/autocompleteMultiselect.js @@ -0,0 +1,194 @@ +'use strict'; + +const color = require('kleur'); +const { cursor } = require('sisteransi'); +const MultiselectPrompt = require('./multiselect'); +const { clear, style, figures } = require('../util'); +/** + * MultiselectPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of choice objects + * @param {String} [opts.hint] Hint to display + * @param {String} [opts.warn] Hint shown for disabled choices + * @param {Number} [opts.max] Max choices + * @param {Number} [opts.cursor=0] Cursor start position + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ +class AutocompleteMultiselectPrompt extends MultiselectPrompt { + constructor(opts={}) { + opts.overrideRender = true; + super(opts); + this.inputValue = ''; + this.clear = clear('', this.out.columns); + this.filteredOptions = this.value; + this.render(); + } + + last() { + this.cursor = this.filteredOptions.length - 1; + this.render(); + } + next() { + this.cursor = (this.cursor + 1) % this.filteredOptions.length; + this.render(); + } + + up() { + if (this.cursor === 0) { + this.cursor = this.filteredOptions.length - 1; + } else { + this.cursor--; + } + this.render(); + } + + down() { + if (this.cursor === this.filteredOptions.length - 1) { + this.cursor = 0; + } else { + this.cursor++; + } + this.render(); + } + + left() { + this.filteredOptions[this.cursor].selected = false; + this.render(); + } + + right() { + if (this.value.filter(e => e.selected).length >= this.maxChoices) return this.bell(); + this.filteredOptions[this.cursor].selected = true; + this.render(); + } + + delete() { + if (this.inputValue.length) { + this.inputValue = this.inputValue.substr(0, this.inputValue.length - 1); + this.updateFilteredOptions(); + } + } + + updateFilteredOptions() { + const currentHighlight = this.filteredOptions[this.cursor]; + this.filteredOptions = this.value + .filter(v => { + if (this.inputValue) { + if (typeof v.title === 'string') { + if (v.title.toLowerCase().includes(this.inputValue.toLowerCase())) { + return true; + } + } + if (typeof v.value === 'string') { + if (v.value.toLowerCase().includes(this.inputValue.toLowerCase())) { + return true; + } + } + return false; + } + return true; + }); + const newHighlightIndex = this.filteredOptions.findIndex(v => v === currentHighlight) + this.cursor = newHighlightIndex < 0 ? 0 : newHighlightIndex; + this.render(); + } + + handleSpaceToggle() { + const v = this.filteredOptions[this.cursor]; + + if (v.selected) { + v.selected = false; + this.render(); + } else if (v.disabled || this.value.filter(e => e.selected).length >= this.maxChoices) { + return this.bell(); + } else { + v.selected = true; + this.render(); + } + } + + handleInputChange(c) { + this.inputValue = this.inputValue + c; + this.updateFilteredOptions(); + } + + _(c, key) { + if (c === ' ') { + this.handleSpaceToggle(); + } else { + this.handleInputChange(c); + } + } + + renderInstructions() { + if (this.instructions === undefined || this.instructions) { + if (typeof this.instructions === 'string') { + return this.instructions; + } + return ` +Instructions: + ${figures.arrowUp}/${figures.arrowDown}: Highlight option + ${figures.arrowLeft}/${figures.arrowRight}/[space]: Toggle selection + [a,b,c]/delete: Filter choices + enter/return: Complete answer +`; + } + return ''; + } + + renderCurrentInput() { + return ` +Filtered results for: ${this.inputValue ? this.inputValue : color.gray('Enter something to filter')}\n`; + } + + renderOption(cursor, v, i) { + let title; + if (v.disabled) title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); + else title = cursor === i ? color.cyan().underline(v.title) : v.title; + return (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + title + } + + renderDoneOrInstructions() { + if (this.done) { + return this.value + .filter(e => e.selected) + .map(v => v.title) + .join(', '); + } + + const output = [color.gray(this.hint), this.renderInstructions(), this.renderCurrentInput()]; + + if (this.filteredOptions.length && this.filteredOptions[this.cursor].disabled) { + output.push(color.yellow(this.warn)); + } + return output.join(' '); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + super.render(); + + // print prompt + + let prompt = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(false), + this.renderDoneOrInstructions() + ].join(' '); + + if (this.showMinError) { + prompt += color.red(`You must select a minimum of ${this.minSelected} choices.`); + this.showMinError = false; + } + prompt += this.renderOptions(this.filteredOptions); + + this.out.write(this.clear + prompt); + this.clear = clear(prompt, this.out.columns); + } +} + +module.exports = AutocompleteMultiselectPrompt; diff --git a/node_modules/prompts/lib/elements/confirm.js b/node_modules/prompts/lib/elements/confirm.js new file mode 100644 index 0000000..7a9173f --- /dev/null +++ b/node_modules/prompts/lib/elements/confirm.js @@ -0,0 +1,89 @@ +const color = require('kleur'); +const Prompt = require('./prompt'); +const { style, clear } = require('../util'); +const { erase, cursor } = require('sisteransi'); + +/** + * ConfirmPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Boolean} [opts.initial] Default value (true/false) + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.yes] The "Yes" label + * @param {String} [opts.yesOption] The "Yes" option when choosing between yes/no + * @param {String} [opts.no] The "No" label + * @param {String} [opts.noOption] The "No" option when choosing between yes/no + */ +class ConfirmPrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.msg = opts.message; + this.value = opts.initial; + this.initialValue = !!opts.initial; + this.yesMsg = opts.yes || 'yes'; + this.yesOption = opts.yesOption || '(Y/n)'; + this.noMsg = opts.no || 'no'; + this.noOption = opts.noOption || '(y/N)'; + this.render(); + } + + reset() { + this.value = this.initialValue; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + this.value = this.value || false; + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + _(c, key) { + if (c.toLowerCase() === 'y') { + this.value = true; + return this.submit(); + } + if (c.toLowerCase() === 'n') { + this.value = false; + return this.submit(); + } + return this.bell(); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + this.outputText = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(this.done), + this.done ? (this.value ? this.yesMsg : this.noMsg) + : color.gray(this.initialValue ? this.yesOption : this.noOption) + ].join(' '); + + this.out.write(erase.line + cursor.to(0) + this.outputText); + } +} + +module.exports = ConfirmPrompt; diff --git a/node_modules/prompts/lib/elements/date.js b/node_modules/prompts/lib/elements/date.js new file mode 100644 index 0000000..71ff608 --- /dev/null +++ b/node_modules/prompts/lib/elements/date.js @@ -0,0 +1,209 @@ +'use strict'; + +const color = require('kleur'); +const Prompt = require('./prompt'); +const { style, clear, figures } = require('../util'); +const { erase, cursor } = require('sisteransi'); +const { DatePart, Meridiem, Day, Hours, Milliseconds, Minutes, Month, Seconds, Year } = require('../dateparts'); + +const regex = /\\(.)|"((?:\\["\\]|[^"])+)"|(D[Do]?|d{3,4}|d)|(M{1,4})|(YY(?:YY)?)|([aA])|([Hh]{1,2})|(m{1,2})|(s{1,2})|(S{1,4})|./g; +const regexGroups = { + 1: ({token}) => token.replace(/\\(.)/g, '$1'), + 2: (opts) => new Day(opts), // Day // TODO + 3: (opts) => new Month(opts), // Month + 4: (opts) => new Year(opts), // Year + 5: (opts) => new Meridiem(opts), // AM/PM // TODO (special) + 6: (opts) => new Hours(opts), // Hours + 7: (opts) => new Minutes(opts), // Minutes + 8: (opts) => new Seconds(opts), // Seconds + 9: (opts) => new Milliseconds(opts), // Fractional seconds +} + +const dfltLocales = { + months: 'January,February,March,April,May,June,July,August,September,October,November,December'.split(','), + monthsShort: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), + weekdays: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), + weekdaysShort: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(',') +} + + +/** + * DatePrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Number} [opts.initial] Index of default value + * @param {String} [opts.mask] The format mask + * @param {object} [opts.locales] The date locales + * @param {String} [opts.error] The error message shown on invalid value + * @param {Function} [opts.validate] Function to validate the submitted value + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ +class DatePrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.msg = opts.message; + this.cursor = 0; + this.typed = ''; + this.locales = Object.assign(dfltLocales, opts.locales); + this._date = opts.initial || new Date(); + this.errorMsg = opts.error || 'Please Enter A Valid Value'; + this.validator = opts.validate || (() => true); + this.mask = opts.mask || 'YYYY-MM-DD HH:mm:ss'; + this.clear = clear('', this.out.columns); + this.render(); + } + + get value() { + return this.date + } + + get date() { + return this._date; + } + + set date(date) { + if (date) this._date.setTime(date.getTime()); + } + + set mask(mask) { + let result; + this.parts = []; + while(result = regex.exec(mask)) { + let match = result.shift(); + let idx = result.findIndex(gr => gr != null); + this.parts.push(idx in regexGroups + ? regexGroups[idx]({ token: result[idx] || match, date: this.date, parts: this.parts, locales: this.locales }) + : result[idx] || match); + } + + let parts = this.parts.reduce((arr, i) => { + if (typeof i === 'string' && typeof arr[arr.length - 1] === 'string') + arr[arr.length - 1] += i; + else arr.push(i); + return arr; + }, []); + + this.parts.splice(0); + this.parts.push(...parts); + this.reset(); + } + + moveCursor(n) { + this.typed = ''; + this.cursor = n; + this.fire(); + } + + reset() { + this.moveCursor(this.parts.findIndex(p => p instanceof DatePart)); + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.error = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + async validate() { + let valid = await this.validator(this.value); + if (typeof valid === 'string') { + this.errorMsg = valid; + valid = false; + } + this.error = !valid; + } + + async submit() { + await this.validate(); + if (this.error) { + this.color = 'red'; + this.fire(); + this.render(); + return; + } + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + up() { + this.typed = ''; + this.parts[this.cursor].up(); + this.render(); + } + + down() { + this.typed = ''; + this.parts[this.cursor].down(); + this.render(); + } + + left() { + let prev = this.parts[this.cursor].prev(); + if (prev == null) return this.bell(); + this.moveCursor(this.parts.indexOf(prev)); + this.render(); + } + + right() { + let next = this.parts[this.cursor].next(); + if (next == null) return this.bell(); + this.moveCursor(this.parts.indexOf(next)); + this.render(); + } + + next() { + let next = this.parts[this.cursor].next(); + this.moveCursor(next + ? this.parts.indexOf(next) + : this.parts.findIndex((part) => part instanceof DatePart)); + this.render(); + } + + _(c) { + if (/\d/.test(c)) { + this.typed += c; + this.parts[this.cursor].setTo(this.typed); + this.render(); + } + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + // Print prompt + this.outputText = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(false), + this.parts.reduce((arr, p, idx) => arr.concat(idx === this.cursor && !this.done ? color.cyan().underline(p.toString()) : p), []) + .join('') + ].join(' '); + + // Print error + if (this.error) { + this.outputText += this.errorMsg.split('\n').reduce( + (a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText); + } +} + +module.exports = DatePrompt; diff --git a/node_modules/prompts/lib/elements/index.js b/node_modules/prompts/lib/elements/index.js new file mode 100644 index 0000000..2556dd0 --- /dev/null +++ b/node_modules/prompts/lib/elements/index.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + TextPrompt: require('./text'), + SelectPrompt: require('./select'), + TogglePrompt: require('./toggle'), + DatePrompt: require('./date'), + NumberPrompt: require('./number'), + MultiselectPrompt: require('./multiselect'), + AutocompletePrompt: require('./autocomplete'), + AutocompleteMultiselectPrompt: require('./autocompleteMultiselect'), + ConfirmPrompt: require('./confirm') +}; diff --git a/node_modules/prompts/lib/elements/multiselect.js b/node_modules/prompts/lib/elements/multiselect.js new file mode 100644 index 0000000..99b393f --- /dev/null +++ b/node_modules/prompts/lib/elements/multiselect.js @@ -0,0 +1,271 @@ +'use strict'; + +const color = require('kleur'); +const { cursor } = require('sisteransi'); +const Prompt = require('./prompt'); +const { clear, figures, style, wrap, entriesToDisplay } = require('../util'); + +/** + * MultiselectPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of choice objects + * @param {String} [opts.hint] Hint to display + * @param {String} [opts.warn] Hint shown for disabled choices + * @param {Number} [opts.max] Max choices + * @param {Number} [opts.cursor=0] Cursor start position + * @param {Number} [opts.optionsPerPage=10] Max options to display at once + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ +class MultiselectPrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.msg = opts.message; + this.cursor = opts.cursor || 0; + this.scrollIndex = opts.cursor || 0; + this.hint = opts.hint || ''; + this.warn = opts.warn || '- This option is disabled -'; + this.minSelected = opts.min; + this.showMinError = false; + this.maxChoices = opts.max; + this.instructions = opts.instructions; + this.optionsPerPage = opts.optionsPerPage || 10; + this.value = opts.choices.map((ch, idx) => { + if (typeof ch === 'string') + ch = {title: ch, value: idx}; + return { + title: ch && (ch.title || ch.value || ch), + description: ch && ch.description, + value: ch && (ch.value === undefined ? idx : ch.value), + selected: ch && ch.selected, + disabled: ch && ch.disabled + }; + }); + this.clear = clear('', this.out.columns); + if (!opts.overrideRender) { + this.render(); + } + } + + reset() { + this.value.map(v => !v.selected); + this.cursor = 0; + this.fire(); + this.render(); + } + + selected() { + return this.value.filter(v => v.selected); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + const selected = this.value + .filter(e => e.selected); + if (this.minSelected && selected.length < this.minSelected) { + this.showMinError = true; + this.render(); + } else { + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + } + + first() { + this.cursor = 0; + this.render(); + } + + last() { + this.cursor = this.value.length - 1; + this.render(); + } + next() { + this.cursor = (this.cursor + 1) % this.value.length; + this.render(); + } + + up() { + if (this.cursor === 0) { + this.cursor = this.value.length - 1; + } else { + this.cursor--; + } + this.render(); + } + + down() { + if (this.cursor === this.value.length - 1) { + this.cursor = 0; + } else { + this.cursor++; + } + this.render(); + } + + left() { + this.value[this.cursor].selected = false; + this.render(); + } + + right() { + if (this.value.filter(e => e.selected).length >= this.maxChoices) return this.bell(); + this.value[this.cursor].selected = true; + this.render(); + } + + handleSpaceToggle() { + const v = this.value[this.cursor]; + + if (v.selected) { + v.selected = false; + this.render(); + } else if (v.disabled || this.value.filter(e => e.selected).length >= this.maxChoices) { + return this.bell(); + } else { + v.selected = true; + this.render(); + } + } + + toggleAll() { + if (this.maxChoices !== undefined || this.value[this.cursor].disabled) { + return this.bell(); + } + + const newSelected = !this.value[this.cursor].selected; + this.value.filter(v => !v.disabled).forEach(v => v.selected = newSelected); + this.render(); + } + + _(c, key) { + if (c === ' ') { + this.handleSpaceToggle(); + } else if (c === 'a') { + this.toggleAll(); + } else { + return this.bell(); + } + } + + renderInstructions() { + if (this.instructions === undefined || this.instructions) { + if (typeof this.instructions === 'string') { + return this.instructions; + } + return '\nInstructions:\n' + + ` ${figures.arrowUp}/${figures.arrowDown}: Highlight option\n` + + ` ${figures.arrowLeft}/${figures.arrowRight}/[space]: Toggle selection\n` + + (this.maxChoices === undefined ? ` a: Toggle all\n` : '') + + ` enter/return: Complete answer`; + } + return ''; + } + + renderOption(cursor, v, i, arrowIndicator) { + const prefix = (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + arrowIndicator + ' '; + let title, desc; + + if (v.disabled) { + title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); + } else { + title = cursor === i ? color.cyan().underline(v.title) : v.title; + if (cursor === i && v.description) { + desc = ` - ${v.description}`; + if (prefix.length + title.length + desc.length >= this.out.columns + || v.description.split(/\r?\n/).length > 1) { + desc = '\n' + wrap(v.description, { margin: prefix.length, width: this.out.columns }); + } + } + } + + return prefix + title + color.gray(desc || ''); + } + + // shared with autocompleteMultiselect + paginateOptions(options) { + if (options.length === 0) { + return color.red('No matches for this query.'); + } + + let { startIndex, endIndex } = entriesToDisplay(this.cursor, options.length, this.optionsPerPage); + let prefix, styledOptions = []; + + for (let i = startIndex; i < endIndex; i++) { + if (i === startIndex && startIndex > 0) { + prefix = figures.arrowUp; + } else if (i === endIndex - 1 && endIndex < options.length) { + prefix = figures.arrowDown; + } else { + prefix = ' '; + } + styledOptions.push(this.renderOption(this.cursor, options[i], i, prefix)); + } + + return '\n' + styledOptions.join('\n'); + } + + // shared with autocomleteMultiselect + renderOptions(options) { + if (!this.done) { + return this.paginateOptions(options); + } + return ''; + } + + renderDoneOrInstructions() { + if (this.done) { + return this.value + .filter(e => e.selected) + .map(v => v.title) + .join(', '); + } + + const output = [color.gray(this.hint), this.renderInstructions()]; + + if (this.value[this.cursor].disabled) { + output.push(color.yellow(this.warn)); + } + return output.join(' '); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + super.render(); + + // print prompt + let prompt = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(false), + this.renderDoneOrInstructions() + ].join(' '); + if (this.showMinError) { + prompt += color.red(`You must select a minimum of ${this.minSelected} choices.`); + this.showMinError = false; + } + prompt += this.renderOptions(this.value); + + this.out.write(this.clear + prompt); + this.clear = clear(prompt, this.out.columns); + } +} + +module.exports = MultiselectPrompt; diff --git a/node_modules/prompts/lib/elements/number.js b/node_modules/prompts/lib/elements/number.js new file mode 100644 index 0000000..dc3efe9 --- /dev/null +++ b/node_modules/prompts/lib/elements/number.js @@ -0,0 +1,213 @@ +const color = require('kleur'); +const Prompt = require('./prompt'); +const { cursor, erase } = require('sisteransi'); +const { style, figures, clear, lines } = require('../util'); + +const isNumber = /[0-9]/; +const isDef = any => any !== undefined; +const round = (number, precision) => { + let factor = Math.pow(10, precision); + return Math.round(number * factor) / factor; +} + +/** + * NumberPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {String} [opts.style='default'] Render style + * @param {Number} [opts.initial] Default value + * @param {Number} [opts.max=+Infinity] Max value + * @param {Number} [opts.min=-Infinity] Min value + * @param {Boolean} [opts.float=false] Parse input as floats + * @param {Number} [opts.round=2] Round floats to x decimals + * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys + * @param {Function} [opts.validate] Validate function + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.error] The invalid error label + */ +class NumberPrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.transform = style.render(opts.style); + this.msg = opts.message; + this.initial = isDef(opts.initial) ? opts.initial : ''; + this.float = !!opts.float; + this.round = opts.round || 2; + this.inc = opts.increment || 1; + this.min = isDef(opts.min) ? opts.min : -Infinity; + this.max = isDef(opts.max) ? opts.max : Infinity; + this.errorMsg = opts.error || `Please Enter A Valid Value`; + this.validator = opts.validate || (() => true); + this.color = `cyan`; + this.value = ``; + this.typed = ``; + this.lastHit = 0; + this.render(); + } + + set value(v) { + if (!v && v !== 0) { + this.placeholder = true; + this.rendered = color.gray(this.transform.render(`${this.initial}`)); + this._value = ``; + } else { + this.placeholder = false; + this.rendered = this.transform.render(`${round(v, this.round)}`); + this._value = round(v, this.round); + } + this.fire(); + } + + get value() { + return this._value; + } + + parse(x) { + return this.float ? parseFloat(x) : parseInt(x); + } + + valid(c) { + return c === `-` || c === `.` && this.float || isNumber.test(c) + } + + reset() { + this.typed = ``; + this.value = ``; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + let x = this.value; + this.value = x !== `` ? x : this.initial; + this.done = this.aborted = true; + this.error = false; + this.fire(); + this.render(); + this.out.write(`\n`); + this.close(); + } + + async validate() { + let valid = await this.validator(this.value); + if (typeof valid === `string`) { + this.errorMsg = valid; + valid = false; + } + this.error = !valid; + } + + async submit() { + await this.validate(); + if (this.error) { + this.color = `red`; + this.fire(); + this.render(); + return; + } + let x = this.value; + this.value = x !== `` ? x : this.initial; + this.done = true; + this.aborted = false; + this.error = false; + this.fire(); + this.render(); + this.out.write(`\n`); + this.close(); + } + + up() { + this.typed = ``; + if(this.value === '') { + this.value = this.min - this.inc; + } + if (this.value >= this.max) return this.bell(); + this.value += this.inc; + this.color = `cyan`; + this.fire(); + this.render(); + } + + down() { + this.typed = ``; + if(this.value === '') { + this.value = this.min + this.inc; + } + if (this.value <= this.min) return this.bell(); + this.value -= this.inc; + this.color = `cyan`; + this.fire(); + this.render(); + } + + delete() { + let val = this.value.toString(); + if (val.length === 0) return this.bell(); + this.value = this.parse((val = val.slice(0, -1))) || ``; + if (this.value !== '' && this.value < this.min) { + this.value = this.min; + } + this.color = `cyan`; + this.fire(); + this.render(); + } + + next() { + this.value = this.initial; + this.fire(); + this.render(); + } + + _(c, key) { + if (!this.valid(c)) return this.bell(); + + const now = Date.now(); + if (now - this.lastHit > 1000) this.typed = ``; // 1s elapsed + this.typed += c; + this.lastHit = now; + this.color = `cyan`; + + if (c === `.`) return this.fire(); + + this.value = Math.min(this.parse(this.typed), this.max); + if (this.value > this.max) this.value = this.max; + if (this.value < this.min) this.value = this.min; + this.fire(); + this.render(); + } + + render() { + if (this.closed) return; + if (!this.firstRender) { + if (this.outputError) + this.out.write(cursor.down(lines(this.outputError, this.out.columns) - 1) + clear(this.outputError, this.out.columns)); + this.out.write(clear(this.outputText, this.out.columns)); + } + super.render(); + this.outputError = ''; + + // Print prompt + this.outputText = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(this.done), + !this.done || (!this.done && !this.placeholder) + ? color[this.color]().underline(this.rendered) : this.rendered + ].join(` `); + + // Print error + if (this.error) { + this.outputError += this.errorMsg.split(`\n`) + .reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore); + } +} + +module.exports = NumberPrompt; diff --git a/node_modules/prompts/lib/elements/prompt.js b/node_modules/prompts/lib/elements/prompt.js new file mode 100644 index 0000000..b793330 --- /dev/null +++ b/node_modules/prompts/lib/elements/prompt.js @@ -0,0 +1,68 @@ +'use strict'; + +const readline = require('readline'); +const { action } = require('../util'); +const EventEmitter = require('events'); +const { beep, cursor } = require('sisteransi'); +const color = require('kleur'); + +/** + * Base prompt skeleton + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ +class Prompt extends EventEmitter { + constructor(opts={}) { + super(); + + this.firstRender = true; + this.in = opts.stdin || process.stdin; + this.out = opts.stdout || process.stdout; + this.onRender = (opts.onRender || (() => void 0)).bind(this); + const rl = readline.createInterface({ input:this.in, escapeCodeTimeout:50 }); + readline.emitKeypressEvents(this.in, rl); + + if (this.in.isTTY) this.in.setRawMode(true); + const isSelect = [ 'SelectPrompt', 'MultiselectPrompt' ].indexOf(this.constructor.name) > -1; + const keypress = (str, key) => { + let a = action(key, isSelect); + if (a === false) { + this._ && this._(str, key); + } else if (typeof this[a] === 'function') { + this[a](key); + } else { + this.bell(); + } + }; + + this.close = () => { + this.out.write(cursor.show); + this.in.removeListener('keypress', keypress); + if (this.in.isTTY) this.in.setRawMode(false); + rl.close(); + this.emit(this.aborted ? 'abort' : this.exited ? 'exit' : 'submit', this.value); + this.closed = true; + }; + + this.in.on('keypress', keypress); + } + + fire() { + this.emit('state', { + value: this.value, + aborted: !!this.aborted, + exited: !!this.exited + }); + } + + bell() { + this.out.write(beep); + } + + render() { + this.onRender(color); + if (this.firstRender) this.firstRender = false; + } +} + +module.exports = Prompt; diff --git a/node_modules/prompts/lib/elements/select.js b/node_modules/prompts/lib/elements/select.js new file mode 100644 index 0000000..6d6727f --- /dev/null +++ b/node_modules/prompts/lib/elements/select.js @@ -0,0 +1,175 @@ +'use strict'; + +const color = require('kleur'); +const Prompt = require('./prompt'); +const { style, clear, figures, wrap, entriesToDisplay } = require('../util'); +const { cursor } = require('sisteransi'); + +/** + * SelectPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Array} opts.choices Array of choice objects + * @param {String} [opts.hint] Hint to display + * @param {Number} [opts.initial] Index of default value + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {Number} [opts.optionsPerPage=10] Max options to display at once + */ +class SelectPrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.msg = opts.message; + this.hint = opts.hint || '- Use arrow-keys. Return to submit.'; + this.warn = opts.warn || '- This option is disabled'; + this.cursor = opts.initial || 0; + this.choices = opts.choices.map((ch, idx) => { + if (typeof ch === 'string') + ch = {title: ch, value: idx}; + return { + title: ch && (ch.title || ch.value || ch), + value: ch && (ch.value === undefined ? idx : ch.value), + description: ch && ch.description, + selected: ch && ch.selected, + disabled: ch && ch.disabled + }; + }); + this.optionsPerPage = opts.optionsPerPage || 10; + this.value = (this.choices[this.cursor] || {}).value; + this.clear = clear('', this.out.columns); + this.render(); + } + + moveCursor(n) { + this.cursor = n; + this.value = this.choices[n].value; + this.fire(); + } + + reset() { + this.moveCursor(0); + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + if (!this.selection.disabled) { + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } else + this.bell(); + } + + first() { + this.moveCursor(0); + this.render(); + } + + last() { + this.moveCursor(this.choices.length - 1); + this.render(); + } + + up() { + if (this.cursor === 0) { + this.moveCursor(this.choices.length - 1); + } else { + this.moveCursor(this.cursor - 1); + } + this.render(); + } + + down() { + if (this.cursor === this.choices.length - 1) { + this.moveCursor(0); + } else { + this.moveCursor(this.cursor + 1); + } + this.render(); + } + + next() { + this.moveCursor((this.cursor + 1) % this.choices.length); + this.render(); + } + + _(c, key) { + if (c === ' ') return this.submit(); + } + + get selection() { + return this.choices[this.cursor]; + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + let { startIndex, endIndex } = entriesToDisplay(this.cursor, this.choices.length, this.optionsPerPage); + + // Print prompt + this.outputText = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(false), + this.done ? this.selection.title : this.selection.disabled + ? color.yellow(this.warn) : color.gray(this.hint) + ].join(' '); + + // Print choices + if (!this.done) { + this.outputText += '\n'; + for (let i = startIndex; i < endIndex; i++) { + let title, prefix, desc = '', v = this.choices[i]; + + // Determine whether to display "more choices" indicators + if (i === startIndex && startIndex > 0) { + prefix = figures.arrowUp; + } else if (i === endIndex - 1 && endIndex < this.choices.length) { + prefix = figures.arrowDown; + } else { + prefix = ' '; + } + + if (v.disabled) { + title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); + prefix = (this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' ') + prefix; + } else { + title = this.cursor === i ? color.cyan().underline(v.title) : v.title; + prefix = (this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; + if (v.description && this.cursor === i) { + desc = ` - ${v.description}`; + if (prefix.length + title.length + desc.length >= this.out.columns + || v.description.split(/\r?\n/).length > 1) { + desc = '\n' + wrap(v.description, { margin: 3, width: this.out.columns }); + } + } + } + + this.outputText += `${prefix} ${title}${color.gray(desc)}\n`; + } + } + + this.out.write(this.outputText); + } +} + +module.exports = SelectPrompt; diff --git a/node_modules/prompts/lib/elements/text.js b/node_modules/prompts/lib/elements/text.js new file mode 100644 index 0000000..ee78181 --- /dev/null +++ b/node_modules/prompts/lib/elements/text.js @@ -0,0 +1,208 @@ +const color = require('kleur'); +const Prompt = require('./prompt'); +const { erase, cursor } = require('sisteransi'); +const { style, clear, lines, figures } = require('../util'); + +/** + * TextPrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {String} [opts.style='default'] Render style + * @param {String} [opts.initial] Default value + * @param {Function} [opts.validate] Validate function + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + * @param {String} [opts.error] The invalid error label + */ +class TextPrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.transform = style.render(opts.style); + this.scale = this.transform.scale; + this.msg = opts.message; + this.initial = opts.initial || ``; + this.validator = opts.validate || (() => true); + this.value = ``; + this.errorMsg = opts.error || `Please Enter A Valid Value`; + this.cursor = Number(!!this.initial); + this.cursorOffset = 0; + this.clear = clear(``, this.out.columns); + this.render(); + } + + set value(v) { + if (!v && this.initial) { + this.placeholder = true; + this.rendered = color.gray(this.transform.render(this.initial)); + } else { + this.placeholder = false; + this.rendered = this.transform.render(v); + } + this._value = v; + this.fire(); + } + + get value() { + return this._value; + } + + reset() { + this.value = ``; + this.cursor = Number(!!this.initial); + this.cursorOffset = 0; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.value = this.value || this.initial; + this.done = this.aborted = true; + this.error = false; + this.red = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + async validate() { + let valid = await this.validator(this.value); + if (typeof valid === `string`) { + this.errorMsg = valid; + valid = false; + } + this.error = !valid; + } + + async submit() { + this.value = this.value || this.initial; + this.cursorOffset = 0; + this.cursor = this.rendered.length; + await this.validate(); + if (this.error) { + this.red = true; + this.fire(); + this.render(); + return; + } + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + next() { + if (!this.placeholder) return this.bell(); + this.value = this.initial; + this.cursor = this.rendered.length; + this.fire(); + this.render(); + } + + moveCursor(n) { + if (this.placeholder) return; + this.cursor = this.cursor+n; + this.cursorOffset += n; + } + + _(c, key) { + let s1 = this.value.slice(0, this.cursor); + let s2 = this.value.slice(this.cursor); + this.value = `${s1}${c}${s2}`; + this.red = false; + this.cursor = this.placeholder ? 0 : s1.length+1; + this.render(); + } + + delete() { + if (this.isCursorAtStart()) return this.bell(); + let s1 = this.value.slice(0, this.cursor-1); + let s2 = this.value.slice(this.cursor); + this.value = `${s1}${s2}`; + this.red = false; + if (this.isCursorAtStart()) { + this.cursorOffset = 0 + } else { + this.cursorOffset++; + this.moveCursor(-1); + } + this.render(); + } + + deleteForward() { + if(this.cursor*this.scale >= this.rendered.length || this.placeholder) return this.bell(); + let s1 = this.value.slice(0, this.cursor); + let s2 = this.value.slice(this.cursor+1); + this.value = `${s1}${s2}`; + this.red = false; + if (this.isCursorAtEnd()) { + this.cursorOffset = 0; + } else { + this.cursorOffset++; + } + this.render(); + } + + first() { + this.cursor = 0; + this.render(); + } + + last() { + this.cursor = this.value.length; + this.render(); + } + + left() { + if (this.cursor <= 0 || this.placeholder) return this.bell(); + this.moveCursor(-1); + this.render(); + } + + right() { + if (this.cursor*this.scale >= this.rendered.length || this.placeholder) return this.bell(); + this.moveCursor(1); + this.render(); + } + + isCursorAtStart() { + return this.cursor === 0 || (this.placeholder && this.cursor === 1); + } + + isCursorAtEnd() { + return this.cursor === this.rendered.length || (this.placeholder && this.cursor === this.rendered.length + 1) + } + + render() { + if (this.closed) return; + if (!this.firstRender) { + if (this.outputError) + this.out.write(cursor.down(lines(this.outputError, this.out.columns) - 1) + clear(this.outputError, this.out.columns)); + this.out.write(clear(this.outputText, this.out.columns)); + } + super.render(); + this.outputError = ''; + + this.outputText = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(this.done), + this.red ? color.red(this.rendered) : this.rendered + ].join(` `); + + if (this.error) { + this.outputError += this.errorMsg.split(`\n`) + .reduce((a, l, i) => a + `\n${i ? ' ' : figures.pointerSmall} ${color.red().italic(l)}`, ``); + } + + this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore + cursor.move(this.cursorOffset, 0)); + } +} + +module.exports = TextPrompt;
\ No newline at end of file diff --git a/node_modules/prompts/lib/elements/toggle.js b/node_modules/prompts/lib/elements/toggle.js new file mode 100644 index 0000000..bad612c --- /dev/null +++ b/node_modules/prompts/lib/elements/toggle.js @@ -0,0 +1,118 @@ +const color = require('kleur'); +const Prompt = require('./prompt'); +const { style, clear } = require('../util'); +const { cursor, erase } = require('sisteransi'); + +/** + * TogglePrompt Base Element + * @param {Object} opts Options + * @param {String} opts.message Message + * @param {Boolean} [opts.initial=false] Default value + * @param {String} [opts.active='no'] Active label + * @param {String} [opts.inactive='off'] Inactive label + * @param {Stream} [opts.stdin] The Readable stream to listen to + * @param {Stream} [opts.stdout] The Writable stream to write readline data to + */ +class TogglePrompt extends Prompt { + constructor(opts={}) { + super(opts); + this.msg = opts.message; + this.value = !!opts.initial; + this.active = opts.active || 'on'; + this.inactive = opts.inactive || 'off'; + this.initialValue = this.value; + this.render(); + } + + reset() { + this.value = this.initialValue; + this.fire(); + this.render(); + } + + exit() { + this.abort(); + } + + abort() { + this.done = this.aborted = true; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + submit() { + this.done = true; + this.aborted = false; + this.fire(); + this.render(); + this.out.write('\n'); + this.close(); + } + + deactivate() { + if (this.value === false) return this.bell(); + this.value = false; + this.render(); + } + + activate() { + if (this.value === true) return this.bell(); + this.value = true; + this.render(); + } + + delete() { + this.deactivate(); + } + left() { + this.deactivate(); + } + right() { + this.activate(); + } + down() { + this.deactivate(); + } + up() { + this.activate(); + } + + next() { + this.value = !this.value; + this.fire(); + this.render(); + } + + _(c, key) { + if (c === ' ') { + this.value = !this.value; + } else if (c === '1') { + this.value = true; + } else if (c === '0') { + this.value = false; + } else return this.bell(); + this.render(); + } + + render() { + if (this.closed) return; + if (this.firstRender) this.out.write(cursor.hide); + else this.out.write(clear(this.outputText, this.out.columns)); + super.render(); + + this.outputText = [ + style.symbol(this.done, this.aborted), + color.bold(this.msg), + style.delimiter(this.done), + this.value ? this.inactive : color.cyan().underline(this.inactive), + color.gray('/'), + this.value ? color.cyan().underline(this.active) : this.active + ].join(' '); + + this.out.write(erase.line + cursor.to(0) + this.outputText); + } +} + +module.exports = TogglePrompt; diff --git a/node_modules/prompts/lib/index.js b/node_modules/prompts/lib/index.js new file mode 100644 index 0000000..a5374d5 --- /dev/null +++ b/node_modules/prompts/lib/index.js @@ -0,0 +1,98 @@ +'use strict'; + +const prompts = require('./prompts'); + +const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender', 'type']; +const noop = () => {}; + +/** + * Prompt for a series of questions + * @param {Array|Object} questions Single question object or Array of question objects + * @param {Function} [onSubmit] Callback function called on prompt submit + * @param {Function} [onCancel] Callback function called on cancel/abort + * @returns {Object} Object with values from user input + */ +async function prompt(questions=[], { onSubmit=noop, onCancel=noop }={}) { + const answers = {}; + const override = prompt._override || {}; + questions = [].concat(questions); + let answer, question, quit, name, type, lastPrompt; + + const getFormattedAnswer = async (question, answer, skipValidation = false) => { + if (!skipValidation && question.validate && question.validate(answer) !== true) { + return; + } + return question.format ? await question.format(answer, answers) : answer + }; + + for (question of questions) { + ({ name, type } = question); + + // evaluate type first and skip if type is a falsy value + if (typeof type === 'function') { + type = await type(answer, { ...answers }, question) + question['type'] = type + } + if (!type) continue; + + // if property is a function, invoke it unless it's a special function + for (let key in question) { + if (passOn.includes(key)) continue; + let value = question[key]; + question[key] = typeof value === 'function' ? await value(answer, { ...answers }, lastPrompt) : value; + } + + lastPrompt = question; + + if (typeof question.message !== 'string') { + throw new Error('prompt message is required'); + } + + // update vars in case they changed + ({ name, type } = question); + + if (prompts[type] === void 0) { + throw new Error(`prompt type (${type}) is not defined`); + } + + if (override[question.name] !== undefined) { + answer = await getFormattedAnswer(question, override[question.name]); + if (answer !== undefined) { + answers[name] = answer; + continue; + } + } + + try { + // Get the injected answer if there is one or prompt the user + answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : await prompts[type](question); + answers[name] = answer = await getFormattedAnswer(question, answer, true); + quit = await onSubmit(question, answer, answers); + } catch (err) { + quit = !(await onCancel(question, answers)); + } + + if (quit) return answers; + } + + return answers; +} + +function getInjectedAnswer(injected, deafultValue) { + const answer = injected.shift(); + if (answer instanceof Error) { + throw answer; + } + + return (answer === undefined) ? deafultValue : answer; +} + +function inject(answers) { + prompt._injected = (prompt._injected || []).concat(answers); +} + +function override(answers) { + prompt._override = Object.assign({}, answers); +} + +module.exports = Object.assign(prompt, { prompt, prompts, inject, override }); diff --git a/node_modules/prompts/lib/prompts.js b/node_modules/prompts/lib/prompts.js new file mode 100644 index 0000000..9f62556 --- /dev/null +++ b/node_modules/prompts/lib/prompts.js @@ -0,0 +1,206 @@ +'use strict'; +const $ = exports; +const el = require('./elements'); +const noop = v => v; + +function toPrompt(type, args, opts={}) { + return new Promise((res, rej) => { + const p = new el[type](args); + const onAbort = opts.onAbort || noop; + const onSubmit = opts.onSubmit || noop; + const onExit = opts.onExit || noop; + p.on('state', args.onState || noop); + p.on('submit', x => res(onSubmit(x))); + p.on('exit', x => res(onExit(x))); + p.on('abort', x => rej(onAbort(x))); + }); +} + +/** + * Text prompt + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {function} [args.onState] On state change callback + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.text = args => toPrompt('TextPrompt', args); + +/** + * Password prompt with masked input + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {function} [args.onState] On state change callback + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.password = args => { + args.style = 'password'; + return $.text(args); +}; + +/** + * Prompt where input is invisible, like sudo + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {function} [args.onState] On state change callback + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.invisible = args => { + args.style = 'invisible'; + return $.text(args); +}; + +/** + * Number prompt + * @param {string} args.message Prompt message to display + * @param {number} args.initial Default number value + * @param {function} [args.onState] On state change callback + * @param {number} [args.max] Max value + * @param {number} [args.min] Min value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {Boolean} [opts.float=false] Parse input as floats + * @param {Number} [opts.round=2] Round floats to x decimals + * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.number = args => toPrompt('NumberPrompt', args); + +/** + * Date prompt + * @param {string} args.message Prompt message to display + * @param {number} args.initial Default number value + * @param {function} [args.onState] On state change callback + * @param {number} [args.max] Max value + * @param {number} [args.min] Min value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {Boolean} [opts.float=false] Parse input as floats + * @param {Number} [opts.round=2] Round floats to x decimals + * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys + * @param {function} [args.validate] Function to validate user input + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.date = args => toPrompt('DatePrompt', args); + +/** + * Classic yes/no prompt + * @param {string} args.message Prompt message to display + * @param {boolean} [args.initial=false] Default value + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.confirm = args => toPrompt('ConfirmPrompt', args); + +/** + * List prompt, split intput string by `seperator` + * @param {string} args.message Prompt message to display + * @param {string} [args.initial] Default string value + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {string} [args.separator] String separator + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input, in form of an `Array` + */ +$.list = args => { + const sep = args.separator || ','; + return toPrompt('TextPrompt', args, { + onSubmit: str => str.split(sep).map(s => s.trim()) + }); +}; + +/** + * Toggle/switch prompt + * @param {string} args.message Prompt message to display + * @param {boolean} [args.initial=false] Default value + * @param {string} [args.active="on"] Text for `active` state + * @param {string} [args.inactive="off"] Text for `inactive` state + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.toggle = args => toPrompt('TogglePrompt', args); + +/** + * Interactive select prompt + * @param {string} args.message Prompt message to display + * @param {Array} args.choices Array of choices objects `[{ title, value }, ...]` + * @param {number} [args.initial] Index of default value + * @param {String} [args.hint] Hint to display + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.select = args => toPrompt('SelectPrompt', args); + +/** + * Interactive multi-select / autocompleteMultiselect prompt + * @param {string} args.message Prompt message to display + * @param {Array} args.choices Array of choices objects `[{ title, value, [selected] }, ...]` + * @param {number} [args.max] Max select + * @param {string} [args.hint] Hint to display user + * @param {Number} [args.cursor=0] Cursor start position + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.multiselect = args => { + args.choices = [].concat(args.choices || []); + const toSelected = items => items.filter(item => item.selected).map(item => item.value); + return toPrompt('MultiselectPrompt', args, { + onAbort: toSelected, + onSubmit: toSelected + }); +}; + +$.autocompleteMultiselect = args => { + args.choices = [].concat(args.choices || []); + const toSelected = items => items.filter(item => item.selected).map(item => item.value); + return toPrompt('AutocompleteMultiselectPrompt', args, { + onAbort: toSelected, + onSubmit: toSelected + }); +}; + +const byTitle = (input, choices) => Promise.resolve( + choices.filter(item => item.title.slice(0, input.length).toLowerCase() === input.toLowerCase()) +); + +/** + * Interactive auto-complete prompt + * @param {string} args.message Prompt message to display + * @param {Array} args.choices Array of auto-complete choices objects `[{ title, value }, ...]` + * @param {Function} [args.suggest] Function to filter results based on user input. Defaults to sort by `title` + * @param {number} [args.limit=10] Max number of results to show + * @param {string} [args.style="default"] Render style ('default', 'password', 'invisible') + * @param {String} [args.initial] Index of the default value + * @param {boolean} [opts.clearFirst] The first ESCAPE keypress will clear the input + * @param {String} [args.fallback] Fallback message - defaults to initial value + * @param {function} [args.onState] On state change callback + * @param {Stream} [args.stdin] The Readable stream to listen to + * @param {Stream} [args.stdout] The Writable stream to write readline data to + * @returns {Promise} Promise with user input + */ +$.autocomplete = args => { + args.suggest = args.suggest || byTitle; + args.choices = [].concat(args.choices || []); + return toPrompt('AutocompletePrompt', args); +}; diff --git a/node_modules/prompts/lib/util/action.js b/node_modules/prompts/lib/util/action.js new file mode 100644 index 0000000..fefbd94 --- /dev/null +++ b/node_modules/prompts/lib/util/action.js @@ -0,0 +1,39 @@ +'use strict'; + +module.exports = (key, isSelect) => { + if (key.meta && key.name !== 'escape') return; + + if (key.ctrl) { + if (key.name === 'a') return 'first'; + if (key.name === 'c') return 'abort'; + if (key.name === 'd') return 'abort'; + if (key.name === 'e') return 'last'; + if (key.name === 'g') return 'reset'; + } + + if (isSelect) { + if (key.name === 'j') return 'down'; + if (key.name === 'k') return 'up'; + } + + if (key.name === 'return') return 'submit'; + if (key.name === 'enter') return 'submit'; // ctrl + J + if (key.name === 'backspace') return 'delete'; + if (key.name === 'delete') return 'deleteForward'; + if (key.name === 'abort') return 'abort'; + if (key.name === 'escape') return 'exit'; + if (key.name === 'tab') return 'next'; + if (key.name === 'pagedown') return 'nextPage'; + if (key.name === 'pageup') return 'prevPage'; + // TODO create home() in prompt types (e.g. TextPrompt) + if (key.name === 'home') return 'home'; + // TODO create end() in prompt types (e.g. TextPrompt) + if (key.name === 'end') return 'end'; + + if (key.name === 'up') return 'up'; + if (key.name === 'down') return 'down'; + if (key.name === 'right') return 'right'; + if (key.name === 'left') return 'left'; + + return false; +}; diff --git a/node_modules/prompts/lib/util/clear.js b/node_modules/prompts/lib/util/clear.js new file mode 100644 index 0000000..e4772d5 --- /dev/null +++ b/node_modules/prompts/lib/util/clear.js @@ -0,0 +1,22 @@ +'use strict'; + +const strip = require('./strip'); +const { erase, cursor } = require('sisteransi'); + +const width = str => [...strip(str)].length; + +/** + * @param {string} prompt + * @param {number} perLine + */ +module.exports = function(prompt, perLine) { + if (!perLine) return erase.line + cursor.to(0); + + let rows = 0; + const lines = prompt.split(/\r?\n/); + for (let line of lines) { + rows += 1 + Math.floor(Math.max(width(line) - 1, 0) / perLine); + } + + return erase.lines(rows); +}; diff --git a/node_modules/prompts/lib/util/entriesToDisplay.js b/node_modules/prompts/lib/util/entriesToDisplay.js new file mode 100644 index 0000000..5f6efbb --- /dev/null +++ b/node_modules/prompts/lib/util/entriesToDisplay.js @@ -0,0 +1,21 @@ +'use strict'; + +/** + * Determine what entries should be displayed on the screen, based on the + * currently selected index and the maximum visible. Used in list-based + * prompts like `select` and `multiselect`. + * + * @param {number} cursor the currently selected entry + * @param {number} total the total entries available to display + * @param {number} [maxVisible] the number of entries that can be displayed + */ +module.exports = (cursor, total, maxVisible) => { + maxVisible = maxVisible || total; + + let startIndex = Math.min(total- maxVisible, cursor - Math.floor(maxVisible / 2)); + if (startIndex < 0) startIndex = 0; + + let endIndex = Math.min(startIndex + maxVisible, total); + + return { startIndex, endIndex }; +}; diff --git a/node_modules/prompts/lib/util/figures.js b/node_modules/prompts/lib/util/figures.js new file mode 100644 index 0000000..cd31b88 --- /dev/null +++ b/node_modules/prompts/lib/util/figures.js @@ -0,0 +1,33 @@ +'use strict'; + + const main = { + arrowUp: '↑', + arrowDown: '↓', + arrowLeft: '←', + arrowRight: '→', + radioOn: '◉', + radioOff: '◯', + tick: '✔', + cross: '✖', + ellipsis: '…', + pointerSmall: '›', + line: '─', + pointer: '❯' +}; +const win = { + arrowUp: main.arrowUp, + arrowDown: main.arrowDown, + arrowLeft: main.arrowLeft, + arrowRight: main.arrowRight, + radioOn: '(*)', + radioOff: '( )', + tick: '√', + cross: '×', + ellipsis: '...', + pointerSmall: '»', + line: '─', + pointer: '>' +}; +const figures = process.platform === 'win32' ? win : main; + + module.exports = figures; diff --git a/node_modules/prompts/lib/util/index.js b/node_modules/prompts/lib/util/index.js new file mode 100644 index 0000000..f815986 --- /dev/null +++ b/node_modules/prompts/lib/util/index.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = { + action: require('./action'), + clear: require('./clear'), + style: require('./style'), + strip: require('./strip'), + figures: require('./figures'), + lines: require('./lines'), + wrap: require('./wrap'), + entriesToDisplay: require('./entriesToDisplay') +}; diff --git a/node_modules/prompts/lib/util/lines.js b/node_modules/prompts/lib/util/lines.js new file mode 100644 index 0000000..de30419 --- /dev/null +++ b/node_modules/prompts/lib/util/lines.js @@ -0,0 +1,15 @@ +'use strict'; + +const strip = require('./strip'); + +/** + * @param {string} msg + * @param {number} perLine + */ +module.exports = function (msg, perLine) { + let lines = String(strip(msg) || '').split(/\r?\n/); + + if (!perLine) return lines.length; + return lines.map(l => Math.ceil(l.length / perLine)) + .reduce((a, b) => a + b); +}; diff --git a/node_modules/prompts/lib/util/strip.js b/node_modules/prompts/lib/util/strip.js new file mode 100644 index 0000000..8ebf4cb --- /dev/null +++ b/node_modules/prompts/lib/util/strip.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = str => { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))' + ].join('|'); + + const RGX = new RegExp(pattern, 'g'); + return typeof str === 'string' ? str.replace(RGX, '') : str; +}; diff --git a/node_modules/prompts/lib/util/style.js b/node_modules/prompts/lib/util/style.js new file mode 100644 index 0000000..1851cc7 --- /dev/null +++ b/node_modules/prompts/lib/util/style.js @@ -0,0 +1,40 @@ +'use strict'; + +const c = require('kleur'); +const figures = require('./figures'); + +// rendering user input. +const styles = Object.freeze({ + password: { scale: 1, render: input => '*'.repeat(input.length) }, + emoji: { scale: 2, render: input => '😃'.repeat(input.length) }, + invisible: { scale: 0, render: input => '' }, + default: { scale: 1, render: input => `${input}` } +}); +const render = type => styles[type] || styles.default; + +// icon to signalize a prompt. +const symbols = Object.freeze({ + aborted: c.red(figures.cross), + done: c.green(figures.tick), + exited: c.yellow(figures.cross), + default: c.cyan('?') +}); + +const symbol = (done, aborted, exited) => + aborted ? symbols.aborted : exited ? symbols.exited : done ? symbols.done : symbols.default; + +// between the question and the user's input. +const delimiter = completing => + c.gray(completing ? figures.ellipsis : figures.pointerSmall); + +const item = (expandable, expanded) => + c.gray(expandable ? (expanded ? figures.pointerSmall : '+') : figures.line); + +module.exports = { + styles, + render, + symbols, + symbol, + delimiter, + item +}; diff --git a/node_modules/prompts/lib/util/wrap.js b/node_modules/prompts/lib/util/wrap.js new file mode 100644 index 0000000..43b5399 --- /dev/null +++ b/node_modules/prompts/lib/util/wrap.js @@ -0,0 +1,27 @@ +'use strict'; + +/** + * @param {string} msg The message to wrap + * @param {object} opts + * @param {number|string} [opts.margin] Left margin + * @param {number} opts.width Maximum characters per line including the margin + */ +module.exports = (msg, opts = {}) => { + const tab = Number.isSafeInteger(parseInt(opts.margin)) + ? new Array(parseInt(opts.margin)).fill(' ').join('') + : (opts.margin || ''); + + const width = opts.width; + + return (msg || '').split(/\r?\n/g) + .map(line => line + .split(/\s+/g) + .reduce((arr, w) => { + if (w.length + tab.length >= width || arr[arr.length - 1].length + w.length + 1 < width) + arr[arr.length - 1] += ` ${w}`; + else arr.push(`${tab}${w}`); + return arr; + }, [ tab ]) + .join('\n')) + .join('\n'); +}; diff --git a/node_modules/prompts/license b/node_modules/prompts/license new file mode 100644 index 0000000..13dc83c --- /dev/null +++ b/node_modules/prompts/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Terkel Gjervig Nielsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/prompts/package.json b/node_modules/prompts/package.json new file mode 100644 index 0000000..f7ba84b --- /dev/null +++ b/node_modules/prompts/package.json @@ -0,0 +1,53 @@ +{ + "name": "prompts", + "version": "2.4.2", + "description": "Lightweight, beautiful and user-friendly prompts", + "license": "MIT", + "repository": "terkelg/prompts", + "main": "index.js", + "author": { + "name": "Terkel Gjervig", + "email": "terkel@terkel.com", + "url": "https://terkel.com" + }, + "files": [ + "lib", + "dist", + "index.js" + ], + "scripts": { + "start": "node lib/index.js", + "build": "babel lib -d dist", + "prepublishOnly": "npm run build", + "test": "tape test/*.js | tap-spec" + }, + "keywords": [ + "ui", + "prompts", + "cli", + "prompt", + "interface", + "command-line", + "input", + "command", + "stdin", + "menu", + "ask", + "interact" + ], + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "devDependencies": { + "@babel/cli": "^7.12.1", + "@babel/core": "^7.12.3", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "tap-spec": "^2.2.2", + "tape": "^4.13.3" + }, + "engines": { + "node": ">= 6" + } +} diff --git a/node_modules/prompts/readme.md b/node_modules/prompts/readme.md new file mode 100755 index 0000000..4a8b065 --- /dev/null +++ b/node_modules/prompts/readme.md @@ -0,0 +1,882 @@ +<p align="center"> + <img src="https://github.com/terkelg/prompts/raw/master/prompts.png" alt="Prompts" width="500" /> +</p> + +<h1 align="center">❯ Prompts</h1> + +<p align="center"> + <a href="https://npmjs.org/package/prompts"> + <img src="https://img.shields.io/npm/v/prompts.svg" alt="version" /> + </a> + <a href="https://travis-ci.org/terkelg/prompts"> + <img src="https://img.shields.io/travis/terkelg/prompts.svg" alt="travis" /> + </a> + <a href="https://npmjs.org/package/prompts"> + <img src="https://img.shields.io/npm/dm/prompts.svg" alt="downloads" /> + </a> + <!--- + <a href="https://packagephobia.now.sh/result?p=prompts"> + <img src="https://packagephobia.now.sh/badge?p=prompts" alt="install size" /> + </a> + ---> +</p> + +<p align="center"> + <b>Lightweight, beautiful and user-friendly interactive prompts</b><br /> + <sub>>_ Easy to use CLI prompts to enquire users for information▌</sub> +</p> + +<br /> + +* **Simple**: prompts has [no big dependencies](http://npm.anvaka.com/#/view/2d/prompts) nor is it broken into a [dozen](http://npm.anvaka.com/#/view/2d/inquirer) tiny modules that only work well together. +* **User friendly**: prompt uses layout and colors to create beautiful cli interfaces. +* **Promised**: uses promises and `async`/`await`. No callback hell. +* **Flexible**: all prompts are independent and can be used on their own. +* **Testable**: provides a way to submit answers programmatically. +* **Unified**: consistent experience across all [prompts](#-types). + + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + + +## ❯ Install + +``` +$ npm install --save prompts +``` + +> This package supports Node 6 and above + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + +## ❯ Usage + +<img src="https://github.com/terkelg/prompts/raw/master/media/example.gif" alt="example prompt" width="499" height="103" /> + +```js +const prompts = require('prompts'); + +(async () => { + const response = await prompts({ + type: 'number', + name: 'value', + message: 'How old are you?', + validate: value => value < 18 ? `Nightclub is 18+ only` : true + }); + + console.log(response); // => { value: 24 } +})(); +``` + +> See [`example.js`](https://github.com/terkelg/prompts/blob/master/example.js) for more options. + + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + + +## ❯ Examples + +### Single Prompt + +Prompt with a single prompt object. Returns an object with the response. + +```js +const prompts = require('prompts'); + +(async () => { + const response = await prompts({ + type: 'text', + name: 'meaning', + message: 'What is the meaning of life?' + }); + + console.log(response.meaning); +})(); +``` + +### Prompt Chain + +Prompt with a list of prompt objects. Returns an object with the responses. +Make sure to give each prompt a unique `name` property to prevent overwriting values. + +```js +const prompts = require('prompts'); + +const questions = [ + { + type: 'text', + name: 'username', + message: 'What is your GitHub username?' + }, + { + type: 'number', + name: 'age', + message: 'How old are you?' + }, + { + type: 'text', + name: 'about', + message: 'Tell something about yourself', + initial: 'Why should I?' + } +]; + +(async () => { + const response = await prompts(questions); + + // => response => { username, age, about } +})(); +``` + +### Dynamic Prompts + +Prompt properties can be functions too. +Prompt Objects with `type` set to `falsy` values are skipped. + +```js +const prompts = require('prompts'); + +const questions = [ + { + type: 'text', + name: 'dish', + message: 'Do you like pizza?' + }, + { + type: prev => prev == 'pizza' ? 'text' : null, + name: 'topping', + message: 'Name a topping' + } +]; + +(async () => { + const response = await prompts(questions); +})(); +``` + + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + + +## ❯ API + +### prompts(prompts, options) + +Type: `Function`<br> +Returns: `Object` + +Prompter function which takes your [prompt objects](#-prompt-objects) and returns an object with responses. + + +#### prompts + +Type: `Array|Object`<br> + +Array of [prompt objects](#-prompt-objects). + These are the questions the user will be prompted. You can see the list of supported [prompt types here](#-types). + +Prompts can be submitted (<kbd>return</kbd>, <kbd>enter</kbd>) or canceled (<kbd>esc</kbd>, <kbd>abort</kbd>, <kbd>ctrl</kbd>+<kbd>c</kbd>, <kbd>ctrl</kbd>+<kbd>d</kbd>). No property is being defined on the returned response object when a prompt is canceled. + +#### options.onSubmit + +Type: `Function`<br> +Default: `() => {}` + +Callback that's invoked after each prompt submission. +Its signature is `(prompt, answer, answers)` where `prompt` is the current prompt object, `answer` the user answer to the current question and `answers` the user answers so far. Async functions are supported. + +Return `true` to quit the prompt chain and return all collected responses so far, otherwise continue to iterate prompt objects. + +**Example:** +```js +(async () => { + const questions = [{ ... }]; + const onSubmit = (prompt, answer) => console.log(`Thanks I got ${answer} from ${prompt.name}`); + const response = await prompts(questions, { onSubmit }); +})(); +``` + +#### options.onCancel + +Type: `Function`<br> +Default: `() => {}` + +Callback that's invoked when the user cancels/exits the prompt. +Its signature is `(prompt, answers)` where `prompt` is the current prompt object and `answers` the user answers so far. Async functions are supported. + +Return `true` to continue and prevent the prompt loop from aborting. +On cancel responses collected so far are returned. + +**Example:** +```js +(async () => { + const questions = [{ ... }]; + const onCancel = prompt => { + console.log('Never stop prompting!'); + return true; + } + const response = await prompts(questions, { onCancel }); +})(); +``` + +### override + +Type: `Function` + +Preanswer questions by passing an object with answers to `prompts.override`. +Powerful when combined with arguments of process. + +**Example** +```js +const prompts = require('prompts'); +prompts.override(require('yargs').argv); + +(async () => { + const response = await prompts([ + { + type: 'text', + name: 'twitter', + message: `What's your twitter handle?` + }, + { + type: 'multiselect', + name: 'color', + message: 'Pick colors', + choices: [ + { title: 'Red', value: '#ff0000' }, + { title: 'Green', value: '#00ff00' }, + { title: 'Blue', value: '#0000ff' } + ], + } + ]); + + console.log(response); +})(); +``` + +### inject(values) + +Type: `Function`<br> + +Programmatically inject responses. This enables you to prepare the responses ahead of time. +If any injected value is found the prompt is immediately resolved with the injected value. +This feature is intended for testing only. + +#### values + +Type: `Array` + +Array with values to inject. Resolved values are removed from the internal inject array. +Each value can be an array of values in order to provide answers for a question asked multiple times. +If a value is an instance of `Error` it will simulate the user cancelling/exiting the prompt. + +**Example:** +```js +const prompts = require('prompts'); + +prompts.inject([ '@terkelg', ['#ff0000', '#0000ff'] ]); + +(async () => { + const response = await prompts([ + { + type: 'text', + name: 'twitter', + message: `What's your twitter handle?` + }, + { + type: 'multiselect', + name: 'color', + message: 'Pick colors', + choices: [ + { title: 'Red', value: '#ff0000' }, + { title: 'Green', value: '#00ff00' }, + { title: 'Blue', value: '#0000ff' } + ], + } + ]); + + // => { twitter: 'terkelg', color: [ '#ff0000', '#0000ff' ] } +})(); +``` + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + + +## ❯ Prompt Objects + +Prompts Objects are JavaScript objects that define the "questions" and the [type of prompt](#-types). +Almost all prompt objects have the following properties: + +```js +{ + type: String | Function, + name: String | Function, + message: String | Function, + initial: String | Function | Async Function + format: Function | Async Function, + onRender: Function + onState: Function + stdin: Readable + stdout: Writeable +} +``` + +Each property be of type `function` and will be invoked right before prompting the user. + +The function signature is `(prev, values, prompt)`, where `prev` is the value from the previous prompt, +`values` is the response object with all values collected so far and `prompt` is the previous prompt object. + +**Function example:** +```js +{ + type: prev => prev > 3 ? 'confirm' : null, + name: 'confirm', + message: (prev, values) => `Please confirm that you eat ${values.dish} times ${prev} a day?` +} +``` + +The above prompt will be skipped if the value of the previous prompt is less than 3. + +### type + +Type: `String|Function` + +Defines the type of prompt to display. See the list of [prompt types](#-types) for valid values. + +If `type` is a falsy value the prompter will skip that question. +```js +{ + type: null, + name: 'forgetme', + message: `I'll never be shown anyway`, +} +``` + +### name + +Type: `String|Function` + +The response will be saved under this key/property in the returned response object. +In case you have multiple prompts with the same name only the latest response will be stored. + +> Make sure to give prompts unique names if you don't want to overwrite previous values. + +### message + +Type: `String|Function` + +The message to be displayed to the user. + +### initial + +Type: `String|Function` + +Optional default prompt value. Async functions are supported too. + +### format + +Type: `Function` + +Receive the user input and return the formatted value to be used inside the program. +The value returned will be added to the response object. + +The function signature is `(val, values)`, where `val` is the value from the current prompt and +`values` is the current response object in case you need to format based on previous responses. + +**Example:** +```js +{ + type: 'number', + name: 'price', + message: 'Enter price', + format: val => Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD' }).format(val); +} +``` + +### onRender + +Type: `Function` + +Callback for when the prompt is rendered. +The function receives [kleur](https://github.com/lukeed/kleur) as its first argument and `this` refers to the current prompt. + +**Example:** +```js +{ + type: 'number', + message: 'This message will be overridden', + onRender(kleur) { + this.msg = kleur.cyan('Enter a number'); + } +} +``` + +### onState + +Type: `Function` + +Callback for when the state of the current prompt changes. +The function signature is `(state)` where `state` is an object with a snapshot of the current state. +The state object has two properties `value` and `aborted`. E.g `{ value: 'This is ', aborted: false }` + +### stdin and stdout + +Type: `Stream` + +By default, prompts uses `process.stdin` for receiving input and `process.stdout` for writing output. +If you need to use different streams, for instance `process.stderr`, you can set these with the `stdin` and `stdout` properties. + + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + + +## ❯ Types + +* [text](#textmessage-initial-style) +* [password](#passwordmessage-initial) +* [invisible](#invisiblemessage-initial) +* [number](#numbermessage-initial-max-min-style) +* [confirm](#confirmmessage-initial) +* [list](#listmessage-initial) +* [toggle](#togglemessage-initial-active-inactive) +* [select](#selectmessage-choices-initial-hint-warn) +* [multiselect](#multiselectmessage-choices-initial-max-hint-warn) +* [autocompleteMultiselect](#multiselectmessage-choices-initial-max-hint-warn) +* [autocomplete](#autocompletemessage-choices-initial-suggest-limit-style) +* [date](#datemessage-initial-warn) + +*** + +### text(message, [initial], [style]) +> Text prompt for free text input. + +Hit <kbd>tab</kbd> to autocomplete to `initial` value when provided. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/text.gif" alt="text prompt" width="499" height="103" /> + +```js +{ + type: 'text', + name: 'value', + message: `What's your twitter handle?` +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `string` | Default string value | +| style | `string` | Render style (`default`, `password`, `invisible`, `emoji`). Defaults to `default` | +| format | `function` | Receive user input. The returned value will be added to the response object | +| validate | `function` | Receive user input. Should return `true` if the value is valid, and an error message `String` otherwise. If `false` is returned, a default error message is shown | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### password(message, [initial]) +> Password prompt with masked input. + +This prompt is a similar to a prompt of type `'text'` with `style` set to `'password'`. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/password.gif" alt="password prompt" width="499" height="103" /> + +```js +{ + type: 'password', + name: 'value', + message: 'Tell me a secret' +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `string` | Default string value | +| format | `function` | Receive user input. The returned value will be added to the response object | +| validate | `function` | Receive user input. Should return `true` if the value is valid, and an error message `String` otherwise. If `false` is returned, a default error message is shown | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### invisible(message, [initial]) +> Prompts user for invisible text input. + +This prompt is working like `sudo` where the input is invisible. +This prompt is a similar to a prompt of type `'text'` with style set to `'invisible'`. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/invisible.gif" alt="invisible prompt" width="499" height="103" /> + +```js +{ + type: 'invisible', + name: 'value', + message: 'Enter password' +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `string` | Default string value | +| format | `function` | Receive user input. The returned value will be added to the response object | +| validate | `function` | Receive user input. Should return `true` if the value is valid, and an error message `String` otherwise. If `false` is returned, a default error message is shown | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### number(message, initial, [max], [min], [style]) +> Prompts user for number input. + +You can type in numbers and use <kbd>up</kbd>/<kbd>down</kbd> to increase/decrease the value. Only numbers are allowed as input. Hit <kbd>tab</kbd> to autocomplete to `initial` value when provided. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/number.gif" alt="number prompt" width="499" height="103" /> + +```js +{ + type: 'number', + name: 'value', + message: 'How old are you?', + initial: 0, + style: 'default', + min: 2, + max: 10 +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `number` | Default number value | +| format | `function` | Receive user input. The returned value will be added to the response object | +| validate | `function` | Receive user input. Should return `true` if the value is valid, and an error message `String` otherwise. If `false` is returned, a default error message is shown | +| max | `number` | Max value. Defaults to `Infinity` | +| min | `number` | Min value. Defaults to `-infinity` | +| float | `boolean` | Allow floating point inputs. Defaults to `false` | +| round | `number` | Round `float` values to x decimals. Defaults to `2` | +| increment | `number` | Increment step when using <kbd>arrow</kbd> keys. Defaults to `1` | +| style | `string` | Render style (`default`, `password`, `invisible`, `emoji`). Defaults to `default` | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### confirm(message, [initial]) +> Classic yes/no prompt. + +Hit <kbd>y</kbd> or <kbd>n</kbd> to confirm/reject. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/confirm.gif" alt="confirm prompt" width="499" height="103" /> + +```js +{ + type: 'confirm', + name: 'value', + message: 'Can you confirm?', + initial: true +} +``` + + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `boolean` | Default value. Default is `false` | +| format | `function` | Receive user input. The returned value will be added to the response object | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### list(message, [initial]) +> List prompt that return an array. + +Similar to the `text` prompt, but the output is an `Array` containing the +string separated by `separator`. + +```js +{ + type: 'list', + name: 'value', + message: 'Enter keywords', + initial: '', + separator: ',' +} +``` + +<img src="https://github.com/terkelg/prompts/raw/master/media/list.gif" alt="list prompt" width="499" height="103" /> + + +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `boolean` | Default value | +| format | `function` | Receive user input. The returned value will be added to the response object | +| separator | `string` | String separator. Will trim all white-spaces from start and end of string. Defaults to `','` | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### toggle(message, [initial], [active], [inactive]) +> Interactive toggle/switch prompt. + +Use tab or <kbd>arrow keys</kbd>/<kbd>tab</kbd>/<kbd>space</kbd> to switch between options. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/toggle.gif" alt="toggle prompt" width="499" height="103" /> + +```js +{ + type: 'toggle', + name: 'value', + message: 'Can you confirm?', + initial: true, + active: 'yes', + inactive: 'no' +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `boolean` | Default value. Defaults to `false` | +| format | `function` | Receive user input. The returned value will be added to the response object | +| active | `string` | Text for `active` state. Defaults to `'on'` | +| inactive | `string` | Text for `inactive` state. Defaults to `'off'` | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### select(message, choices, [initial], [hint], [warn]) +> Interactive select prompt. + +Use <kbd>up</kbd>/<kbd>down</kbd> to navigate. Use <kbd>tab</kbd> to cycle the list. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/select.gif" alt="select prompt" width="499" height="130" /> + +```js +{ + type: 'select', + name: 'value', + message: 'Pick a color', + choices: [ + { title: 'Red', description: 'This option has a description', value: '#ff0000' }, + { title: 'Green', value: '#00ff00', disabled: true }, + { title: 'Blue', value: '#0000ff' } + ], + initial: 1 +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `number` | Index of default value | +| format | `function` | Receive user input. The returned value will be added to the response object | +| hint | `string` | Hint to display to the user | +| warn | `string` | Message to display when selecting a disabled option | +| choices | `Array` | Array of strings or choices objects `[{ title, description, value, disabled }, ...]`. The choice's index in the array will be used as its value if it is not specified. | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +**↑ back to:** [Prompt types](#-types) + +*** + +### multiselect(message, choices, [initial], [max], [hint], [warn]) +### autocompleteMultiselect(same) +> Interactive multi-select prompt. +> Autocomplete is a searchable multiselect prompt with the same options. Useful for long lists. + +Use <kbd>space</kbd> to toggle select/unselect and <kbd>up</kbd>/<kbd>down</kbd> to navigate. Use <kbd>tab</kbd> to cycle the list. You can also use <kbd>right</kbd> to select and <kbd>left</kbd> to deselect. +By default this prompt returns an `array` containing the **values** of the selected items - not their display title. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/multiselect.gif" alt="multiselect prompt" width="499" height="130" /> + +```js +{ + type: 'multiselect', + name: 'value', + message: 'Pick colors', + choices: [ + { title: 'Red', value: '#ff0000' }, + { title: 'Green', value: '#00ff00', disabled: true }, + { title: 'Blue', value: '#0000ff', selected: true } + ], + max: 2, + hint: '- Space to select. Return to submit' +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| format | `function` | Receive user input. The returned value will be added to the response object | +| instructions | `string` or `boolean` | Prompt instructions to display | +| choices | `Array` | Array of strings or choices objects `[{ title, value, disabled }, ...]`. The choice's index in the array will be used as its value if it is not specified. | +| optionsPerPage | `number` | Number of options displayed per page (default: 10) | +| min | `number` | Min select - will display error | +| max | `number` | Max select | +| hint | `string` | Hint to display to the user | +| warn | `string` | Message to display when selecting a disabled option | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +This is one of the few prompts that don't take a initial value. +If you want to predefine selected values, give the choice object an `selected` property of `true`. + +**↑ back to:** [Prompt types](#-types) + +*** + +### autocomplete(message, choices, [initial], [suggest], [limit], [style]) +> Interactive auto complete prompt. + +The prompt will list options based on user input. Type to filter the list. +Use <kbd>⇧</kbd>/<kbd>⇩</kbd> to navigate. Use <kbd>tab</kbd> to cycle the result. Use <kbd>Page Up</kbd>/<kbd>Page Down</kbd> (on Mac: <kbd>fn</kbd> + <kbd>⇧</kbd> / <kbd>⇩</kbd>) to change page. Hit <kbd>enter</kbd> to select the highlighted item below the prompt. + +The default suggests function is sorting based on the `title` property of the choices. +You can overwrite how choices are being filtered by passing your own suggest function. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/autocomplete.gif" alt="auto complete prompt" width="499" height="163" /> + +```js +{ + type: 'autocomplete', + name: 'value', + message: 'Pick your favorite actor', + choices: [ + { title: 'Cage' }, + { title: 'Clooney', value: 'silver-fox' }, + { title: 'Gyllenhaal' }, + { title: 'Gibson' }, + { title: 'Grant' } + ] +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| format | `function` | Receive user input. The returned value will be added to the response object | +| choices | `Array` | Array of auto-complete choices objects `[{ title, value }, ...]` | +| suggest | `function` | Filter function. Defaults to sort by `title` property. `suggest` should always return a promise. Filters using `title` by default | +| limit | `number` | Max number of results to show. Defaults to `10` | +| style | `string` | Render style (`default`, `password`, `invisible`, `emoji`). Defaults to `'default'` | +| initial | `string \| number` | Default initial value | +| clearFirst | `boolean` | The first ESCAPE keypress will clear the input | +| fallback | `string` | Fallback message when no match is found. Defaults to `initial` value if provided | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with three properties: `value`, `aborted` and `exited` | + +Example on what a `suggest` function might look like: +```js +const suggestByTitle = (input, choices) => + Promise.resolve(choices.filter(i => i.title.slice(0, input.length) === input)) +``` + +**↑ back to:** [Prompt types](#-types) + +*** + +### date(message, [initial], [warn]) +> Interactive date prompt. + +Use <kbd>left</kbd>/<kbd>right</kbd>/<kbd>tab</kbd> to navigate. Use <kbd>up</kbd>/<kbd>down</kbd> to change date. + +#### Example +<img src="https://github.com/terkelg/prompts/raw/master/media/date.gif" alt="date prompt" width="499" height="103" /> + +```js +{ + type: 'date', + name: 'value', + message: 'Pick a date', + initial: new Date(1997, 09, 12), + validate: date => date > Date.now() ? 'Not in the future' : true +} +``` + +#### Options +| Param | Type | Description | +| ----- | :--: | ----------- | +| message | `string` | Prompt message to display | +| initial | `date` | Default date | +| locales | `object` | Use to define custom locales. See below for an example. | +| mask | `string` | The format mask of the date. See below for more information.<br />Default: `YYYY-MM-DD HH:mm:ss` | +| validate | `function` | Receive user input. Should return `true` if the value is valid, and an error message `String` otherwise. If `false` is returned, a default error message is shown | +| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | +| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | + +Default locales: + +```javascript +{ + months: [ + 'January', 'February', 'March', 'April', + 'May', 'June', 'July', 'August', + 'September', 'October', 'November', 'December' + ], + monthsShort: [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ], + weekdays: [ + 'Sunday', 'Monday', 'Tuesday', 'Wednesday', + 'Thursday', 'Friday', 'Saturday' + ], + weekdaysShort: [ + 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' + ] +} +``` +>**Formatting**: See full list of formatting options in the [wiki](https://github.com/terkelg/prompts/wiki/Date-Time-Formatting) + +![split](https://github.com/terkelg/prompts/raw/master/media/split.png) + +**↑ back to:** [Prompt types](#-types) + +*** + +## ❯ Credit +Many of the prompts are based on the work of [derhuerst](https://github.com/derhuerst). + + +## ❯ License + +MIT © [Terkel Gjervig](https://terkel.com) |