Comparing version 2.0.0 to 2.0.1
12
index.js
@@ -42,3 +42,3 @@ 'use strict'; | ||
register(type, fn) { | ||
if (this.utils.isObject(type)) { | ||
if (utils.isObject(type)) { | ||
for (let key of Object.keys(type)) this.register(key, type[key]); | ||
@@ -86,12 +86,2 @@ return this; | ||
for (let key of Object.keys(opts)) { | ||
let val = opts[key]; | ||
if (typeof val === 'function') { | ||
opts[key] = (...args) => { | ||
args.push(this.answers); | ||
return val(...args); | ||
}; | ||
} | ||
} | ||
let { type, name } = question; | ||
@@ -98,0 +88,0 @@ if (typeof type === 'function') type = await type.call(this, question); |
@@ -0,2 +1,8 @@ | ||
'use strict'; | ||
/** | ||
* Actions are mappings from keypress event names to method names | ||
* in the prompts. | ||
*/ | ||
module.exports = { | ||
@@ -39,3 +45,3 @@ ctrl: { | ||
up: 'altUp', | ||
down: 'altDown', | ||
down: 'altDown' | ||
}, | ||
@@ -42,0 +48,0 @@ |
@@ -82,2 +82,5 @@ 'use strict'; | ||
if (after) pos -= after.length; | ||
if (input === '' && initial && !prompt.includes(initial)) { | ||
pos += initial.length; | ||
} | ||
return ansi.cursor.move(pos); | ||
@@ -84,0 +87,0 @@ } |
'use strict'; | ||
const colors = require('ansi-colors'); | ||
const clean = (str = '') => { | ||
return typeof str === 'string' ? str.replace(/^['"]|['"]$/g, '') : ''; | ||
}; | ||
/** | ||
* This file contains the interpolation and rendering logic for | ||
* the Snippet prompt. | ||
*/ | ||
class Item { | ||
constructor(token) { | ||
this.name = token.key; | ||
this.value = token.default || ''; | ||
this.field = token.field || {}; | ||
this.value = clean(token.initial || this.field.initial || ''); | ||
this.message = token.message || this.name; | ||
this.cursor = 0; | ||
this.input = ''; | ||
this.lines = []; | ||
} | ||
} | ||
const tokenize = (options = {}, defaults = {}, fn = token => token) => { | ||
const tokenize = async(options = {}, defaults = {}, fn = token => token) => { | ||
let unique = new Set(); | ||
let fields = options.fields || []; | ||
let input = options.template; | ||
let tokens = []; | ||
let tabstops = []; | ||
let items = []; | ||
@@ -23,2 +34,6 @@ let keys = []; | ||
if (typeof input === 'function') { | ||
input = await input(); | ||
} | ||
let i = -1; | ||
@@ -29,3 +44,3 @@ let next = () => input[++i]; | ||
token.line = line; | ||
tokens.push(token); | ||
tabstops.push(token); | ||
}; | ||
@@ -38,3 +53,3 @@ | ||
if (value === '\r') { | ||
if (/^[^\S\n ]$/.test(value)) { | ||
continue; | ||
@@ -55,4 +70,6 @@ } | ||
if ((value === '$' || value === '#') && peek() === '{') { | ||
value += next(); | ||
if ((value === '$' || value === '#' || value === '{') && peek() === '{') { | ||
let n = next(); | ||
value += n; | ||
let token = { type: 'template', open: value, inner: '', close: '', value }; | ||
@@ -63,2 +80,5 @@ let ch; | ||
if (ch === '}') { | ||
if (peek() === '}') { | ||
ch += next(); | ||
} | ||
token.value += ch; | ||
@@ -70,6 +90,6 @@ token.close = ch; | ||
if (ch === ':') { | ||
token.default = ''; | ||
token.initial = ''; | ||
token.key = token.inner; | ||
} else if (token.default !== void 0) { | ||
token.default += ch; | ||
} else if (token.initial !== void 0) { | ||
token.initial += ch; | ||
} | ||
@@ -81,7 +101,7 @@ | ||
token.template = token.open + (token.default || token.inner) + token.close; | ||
token.template = token.open + (token.initial || token.inner) + token.close; | ||
token.key = token.key || token.inner; | ||
if (defaults.hasOwnProperty(token.key)) { | ||
token.default = defaults[token.key]; | ||
token.initial = defaults[token.key]; | ||
} | ||
@@ -95,10 +115,15 @@ | ||
let item = items.find(ch => ch.name === token.key); | ||
let item = items.find(item => item.name === token.key); | ||
token.field = fields.find(ch => ch.name === token.key); | ||
if (!item) { | ||
items.push(new Item(token)); | ||
item = new Item(token); | ||
items.push(item); | ||
} | ||
item.lines.push(token.line - 1); | ||
continue; | ||
} | ||
let last = tokens[tokens.length - 1]; | ||
let last = tabstops[tabstops.length - 1]; | ||
if (last.type === 'text' && last.line === line) { | ||
@@ -112,13 +137,20 @@ last.value += value; | ||
push({ type: 'eos', value: '' }); | ||
return { tokens, unique, keys, items }; | ||
return { input, tabstops, unique, keys, items }; | ||
}; | ||
module.exports = prompt => { | ||
module.exports = async prompt => { | ||
let options = prompt.options; | ||
let required = new Set(options.required === true ? [] : (options.required || [])); | ||
let defaults = { ...options.values, ...options.initial }; | ||
let { tokens, items, keys } = tokenize(options, defaults); | ||
let { tabstops, items, keys } = await tokenize(options, defaults); | ||
return async(state = {}, isLast = false) => { | ||
let result = createFn('result', prompt, options); | ||
let format = createFn('format', prompt, options); | ||
let isValid = createFn('validate', prompt, options, true); | ||
let isVal = prompt.isValue.bind(prompt); | ||
return async(state = {}, submitted = false) => { | ||
let index = 0; | ||
state.required = required; | ||
state.items = items; | ||
@@ -128,10 +160,11 @@ state.keys = keys; | ||
let format = (value, item) => { | ||
if (typeof options.format === 'function') { | ||
return options.format(value, item, state); | ||
let validate = async(value, state, item, index) => { | ||
let error = await isValid(value, state, item, index); | ||
if (error === false) { | ||
return 'Invalid fields ' + item.name; | ||
} | ||
return value; | ||
return error; | ||
}; | ||
for (let token of tokens) { | ||
for (let token of tabstops) { | ||
let value = token.value; | ||
@@ -147,30 +180,62 @@ let key = token.key; | ||
let item = items.find(ch => ch.name === key); | ||
let val = [item.input, state.values[item.value], item.value, value].find(v => !!v); | ||
if (isLast) { | ||
let result = await format(state.values[key], item, state); | ||
state.output += colors.unstyle(result); | ||
if (options.required === true) { | ||
state.required.add(item.name); | ||
} | ||
let val = [item.input, state.values[item.value], item.value, value].find(isVal); | ||
let field = item.field || {}; | ||
let message = field.message || token.inner; | ||
if (submitted) { | ||
let error = await validate(state.values[key], state, item, index); | ||
if (error && typeof error === 'string') { | ||
state.invalid.add(key); | ||
continue; | ||
} | ||
state.invalid.delete(key); | ||
let res = await result(state.values[key], state, item, index); | ||
state.output += colors.unstyle(res); | ||
continue; | ||
} | ||
item.placeholder = false; | ||
let before = value; | ||
value = await format(value, state, item, index); | ||
if (val !== value) { | ||
state.values[key] = val; | ||
value = prompt.styles.muted(val); | ||
state.missing.delete(message); | ||
} else if (isLast) { | ||
} else if (submitted) { | ||
value = ''; | ||
} else { | ||
state.values[key] = ''; | ||
val = `<${token.inner}>`; | ||
state.values[key] = void 0; | ||
val = `<${message}>`; | ||
value = prompt.styles.primary(val); | ||
item.placeholder = true; | ||
if (state.missing.has(key) || state.invalid.has(key)) { | ||
value = prompt.styles.danger(val); | ||
} else { | ||
value = prompt.styles.primary(val); | ||
if (state.required.has(key)) { | ||
state.missing.add(message); | ||
} | ||
} | ||
if (state.missing.has(message) && state.validating) { | ||
value = prompt.styles.warning(val); | ||
} | ||
if (state.invalid.has(key) && state.validating) { | ||
value = prompt.styles.danger(val); | ||
} | ||
if (index === state.index) { | ||
value = prompt.styles.primary.underline(value); | ||
if (before !== value) { | ||
value = colors.underline(value); | ||
} else { | ||
value = prompt.styles.heading(colors.unstyle(value)); | ||
} | ||
} | ||
@@ -185,4 +250,37 @@ | ||
} | ||
let lines = state.output.split('\n').map(l => ' ' + l); | ||
let len = items.length; | ||
let done = 0; | ||
for (let item of items) { | ||
if (state.invalid.has(item.name)) { | ||
item.lines.forEach(i => { | ||
if (lines[i][0] !== ' ') return; | ||
lines[i] = state.styles.danger(state.symbols.bullet) + lines[i].slice(1); | ||
}); | ||
} | ||
if (prompt.isValue(state.values[item.name])) { | ||
done++; | ||
} | ||
} | ||
state.completed = ((done / len) * 100).toFixed(0); | ||
state.output = lines.join('\n'); | ||
return state.output; | ||
}; | ||
}; | ||
function createFn(prop, prompt, options, fallback) { | ||
return (value, state, item, index) => { | ||
if (typeof item.field[prop] === 'function') { | ||
return item.field[prop].call(prompt, value, state, item, index); | ||
} | ||
if (typeof options[prop] === 'function') { | ||
return options[prop].call(prompt, value, state, item, index); | ||
} | ||
return fallback || value; | ||
}; | ||
} |
@@ -6,13 +6,25 @@ 'use strict'; | ||
/** | ||
* Render a placeholder value with cursor and styling based on the | ||
* position of the cursor. This function creates the illusion that | ||
* | ||
* | ||
* @param {Object} `prompt` Prompt instance | ||
* @param {String} `input` | ||
* @param {String} `initial` | ||
* @param {Number} `pos` | ||
* @param {Boolean} `showCursor` | ||
* @return {String} Returns the styled placeholder string. | ||
* @api public | ||
*/ | ||
module.exports = (prompt, input = '', initial = '', pos, showCursor = true) => { | ||
prompt.cursorHide(); | ||
let inverse = utils.inverse(prompt.styles.primary); | ||
let output = input; | ||
initial = utils.isPrimitive(initial) ? String(initial) : ''; | ||
input = utils.isPrimitive(input) ? String(input) : ''; | ||
initial = utils.isPrimitive(initial) ? `${initial}` : ''; | ||
input = utils.isPrimitive(input) ? `${input}` : ''; | ||
if (!initial) { | ||
return input; | ||
} | ||
let placeholder = initial && initial.startsWith(input) && initial !== input; | ||
@@ -19,0 +31,0 @@ let cursor = placeholder ? inverse(colors.black(initial[input.length])) : inverse(' '); |
@@ -26,9 +26,9 @@ 'use strict'; | ||
this.state = new State(this); | ||
this.option('format'); | ||
this.option('validate', () => true); | ||
this.option('result', val => val); | ||
this.option('stdout', process.stdout); | ||
this.option('stdin', process.stdin); | ||
this.option('initial'); | ||
this.option('scale', 1); | ||
this.initial = options.initial; | ||
this.stdout = options.stdout || process.stdout; | ||
this.stdin = options.stdin || process.stdin; | ||
this.scale = options.scale || 1; | ||
this.validate = (this.options.validate || this.validate).bind(this); | ||
this.format = (this.options.format || this.format).bind(this); | ||
this.result = (this.options.result || this.result).bind(this); | ||
this.setMaxListeners(0); | ||
@@ -77,3 +77,3 @@ } | ||
if ((!buffer && !lines) || this.options.show === false) return; | ||
this.stdout.write(ansi.cursor.down(lines) + ansi.clear(buffer)); | ||
this.stdout.write(ansi.cursor.down(lines) + ansi.clear(buffer, this.width)); | ||
} | ||
@@ -113,4 +113,5 @@ | ||
this.state.submitted = true; | ||
this.state.validating = true; | ||
let result = await this.validate(this.value); | ||
let result = this.state.error || await this.validate(this.value, this.state); | ||
if (result !== true) { | ||
@@ -120,6 +121,10 @@ let error = this.symbols.pointer + ' ' + result.trim(); | ||
this.state.submitted = false; | ||
this.render(); | ||
return this.alert(); | ||
await this.render(); | ||
await this.alert(); | ||
this.state.validating = false; | ||
this.state.error = void 0; | ||
return; | ||
} | ||
this.state.validating = false; | ||
this.value = this.result(this.value); | ||
@@ -161,2 +166,5 @@ await this.render(); | ||
async initialize() { | ||
if (typeof this.options.initial === 'function') { | ||
this.initial = await this.options.initial.call(this); | ||
} | ||
await this.start(); | ||
@@ -180,8 +188,8 @@ await this.render(); | ||
async element(name, choice, i) { | ||
let timer = this.timers && this.timers[name]; | ||
let state = this.state; | ||
let { options, state, symbols, timers } = this; | ||
let timer = timers && timers[name]; | ||
state.timer = timer; | ||
let value = this.options[name] || state[name] || this.symbols[name]; | ||
let item = choice && choice[name] ? choice[name] : value; | ||
let res = await utils.resolve(state, item, state, choice, i); | ||
let value = options[name] || state[name] || symbols[name]; | ||
let val = choice && choice[name] ? choice[name] : value; | ||
let res = await utils.resolve(state, val, state, choice, i); | ||
if (!res && choice && choice[name]) { | ||
@@ -261,2 +269,6 @@ return utils.resolve(state, value, state, choice, i); | ||
body() { | ||
return null; | ||
} | ||
footer() { | ||
@@ -292,2 +304,10 @@ if (this.state.status === 'pending') { | ||
result(value) { | ||
return value; | ||
} | ||
validate() { | ||
return true; | ||
} | ||
isValue(value) { | ||
@@ -301,9 +321,10 @@ return !!value; | ||
option(key, fallback) { | ||
let value = [this.options[key], this[key], fallback].find(v => v != null); | ||
if (typeof value === 'function') value = value.bind(this); | ||
this[key] = value; | ||
return this; | ||
get base() { | ||
return Prompt.prototype; | ||
} | ||
get style() { | ||
return this.styles[this.state.status]; | ||
} | ||
get height() { | ||
@@ -310,0 +331,0 @@ return this.options.rows || utils.height(this.stdout, 25); |
@@ -6,6 +6,10 @@ 'use strict'; | ||
const highlight = (input, color = colors.red) => { | ||
const highlight = (input, color) => { | ||
let val = input.toLowerCase(); | ||
return str => { | ||
let i = str.indexOf(input); | ||
return str.slice(0, i) + color(input) + str.slice(i + input.length); | ||
let s = str.toLowerCase(); | ||
let i = s.indexOf(val); | ||
let colored = color(str.slice(i, i + val.length)); | ||
return str.slice(0, i) + colored + str.slice(i + val.length); | ||
}; | ||
@@ -15,3 +19,3 @@ }; | ||
class AutoComplete extends Select { | ||
constructor(options = {}) { | ||
constructor(options) { | ||
super(options); | ||
@@ -48,2 +52,9 @@ this.cursorShow(); | ||
deleteForward() { | ||
let { cursor, input } = this.state; | ||
if (input[cursor] === void 0) return this.alert(); | ||
this.state.input = `${input}`.slice(0, cursor) + `${input}`.slice(cursor + 1); | ||
return this.complete(); | ||
} | ||
async complete() { | ||
@@ -83,3 +94,4 @@ this.completing = true; | ||
if (this.state.status !== 'pending') return super.render(); | ||
let color = highlight(this.state.input, this.styles.complement); | ||
let style = this.options.highlight || this.styles.complement; | ||
let color = highlight(this.state.input, style); | ||
let choices = this.choices; | ||
@@ -86,0 +98,0 @@ this.choices = choices.map(ch => ({ ...ch, message: color(ch.message) })); |
@@ -6,3 +6,3 @@ 'use strict'; | ||
class ConfirmPrompt extends BooleanPrompt { | ||
constructor(options = {}) { | ||
constructor(options) { | ||
super(options); | ||
@@ -9,0 +9,0 @@ this.default = this.initial ? '(Y/n)' : '(y/N)'; |
@@ -11,2 +11,3 @@ 'use strict'; | ||
define('Confirm', () => require('./confirm')); | ||
define('Form', () => require('./form')); | ||
define('Input', () => require('./input')); | ||
@@ -20,5 +21,3 @@ define('Invisible', () => require('./invisible')); | ||
define('Snippet', () => require('./snippet')); | ||
define('Transition', () => require('./transition')); | ||
define('Transition2', () => require('./transition2')); | ||
define('Transition3', () => require('./transition3')); | ||
define('Sort', () => require('./sort')); | ||
define('Text', () => require('./text')); |
@@ -23,3 +23,3 @@ 'use strict'; | ||
async renderChoice(choice, i) { | ||
await super.onChoice(choice, i); | ||
await this.onChoice(choice, i); | ||
@@ -35,4 +35,5 @@ let focused = this.index === i; | ||
let ind = choice.indent; | ||
let message = await this.resolve(choice.message, this.state, choice, i); | ||
let line = () => [pointer + indicator, message, hint].filter(Boolean).join(' '); | ||
let line = () => [ind + pointer + indicator, message, hint].filter(Boolean).join(' '); | ||
@@ -45,3 +46,3 @@ if (choice.disabled) { | ||
if (focused) { | ||
message = this.styles.em(message); | ||
message = this.styles.heading(message); | ||
} | ||
@@ -78,11 +79,11 @@ return line(); | ||
let header = await this.header(); | ||
let input = await this.format(); | ||
let output = await this.format(); | ||
let help = await this.error() || await this.hint(); | ||
let choices = await this.renderChoices(); | ||
let body = await this.renderChoices(); | ||
let footer = await this.footer(); | ||
if (input || !help) prompt += ' ' + input; | ||
if (output || !help) prompt += ' ' + output; | ||
if (help && !prompt.includes(help)) prompt += ' ' + help; | ||
if (submitted && this.options.multiple && !input && !choices) { | ||
if (submitted && this.options.multiple && !output && !body) { | ||
prompt += this.styles.danger('No items were selected'); | ||
@@ -92,3 +93,3 @@ } | ||
this.clear(size); | ||
this.write([header, prompt, choices, footer].filter(Boolean).join('\n')); | ||
this.write([header, prompt, body, footer].filter(Boolean).join('\n')); | ||
this.restore(); | ||
@@ -95,0 +96,0 @@ } |
@@ -10,10 +10,22 @@ 'use strict'; | ||
super(options); | ||
this.cursorHide(); | ||
this.reset(true); | ||
} | ||
async initialize() { | ||
this.interpolate = await interpolate(this); | ||
await super.initialize(); | ||
} | ||
async reset(first) { | ||
this.state.keys = []; | ||
this.state.required = new Set(); | ||
this.state.missing = new Set(); | ||
this.state.invalid = new Set(); | ||
this.state.errors = []; | ||
this.state.completed = 0; | ||
this.state.values = {}; | ||
this.interpolate = interpolate(this); | ||
this.cursorHide(); | ||
if (!first) { | ||
await this.initialize(); | ||
await this.render(); | ||
} | ||
} | ||
@@ -41,3 +53,2 @@ | ||
this.moveCursor(1); | ||
this.update(); | ||
this.render(); | ||
@@ -53,3 +64,2 @@ } | ||
this.moveCursor(-1); | ||
this.update(); | ||
this.render(); | ||
@@ -108,16 +118,10 @@ } | ||
update() { | ||
if (this.state.key && this.input) { | ||
this.state.missing.delete(this.state.key); | ||
return; | ||
format(value) { | ||
let color = this.state.completed < 100 ? this.styles.warning : this.styles.success; | ||
if (this.state.submitted === true && this.state.completed !== 100) { | ||
color = this.styles.danger; | ||
} | ||
if (this.state.key && this.state.required.has(this.state.key)) { | ||
this.state.missing.add(this.state.key); | ||
} | ||
return color(`${this.state.completed}% completed`); | ||
} | ||
format(value) { | ||
return this.state.answered ? '' : super.format(value); | ||
} | ||
async render() { | ||
@@ -134,3 +138,4 @@ let { index, keys = [], submitted, size } = this.state; | ||
let header = await this.header(); | ||
let help = await this.error() || await this.hint(); | ||
let error = await this.error() || ''; | ||
let hint = await this.hint() || ''; | ||
let body = submitted ? '' : await this.interpolate(this.state); | ||
@@ -141,8 +146,7 @@ | ||
let footer = await this.footer(); | ||
if (input) prompt += ' ' + input; | ||
if (hint && !input && this.state.completed === 0) prompt += ' ' + hint; | ||
if (input || !help) prompt += ' ' + input; | ||
if (help && !prompt.includes(help)) prompt += ' ' + help; | ||
this.clear(size); | ||
this.write([header, prompt, body, footer].filter(Boolean).join('\n')); | ||
this.write([header, prompt, body, footer, error.trim()].filter(Boolean).join('\n')); | ||
this.restore(); | ||
@@ -152,3 +156,4 @@ } | ||
getItem(name) { | ||
let item = this.state.items.find(ch => ch.name === this.state.keys[this.state.index]); | ||
let { items, keys, index } = this.state; | ||
let item = items.find(ch => ch.name === keys[index]); | ||
if (item && item.input != null) { | ||
@@ -162,4 +167,16 @@ this.input = item.input; | ||
async submit() { | ||
let result = colors.unstyle(await this.interpolate(this.state, true)); | ||
let values = this.state.values; | ||
await this.interpolate(this.state, true); | ||
let { invalid, missing, output, values } = this.state; | ||
if (invalid.size) { | ||
this.state.error = 'Invalid: ' + [...invalid.keys()].join(', '); | ||
return super.submit(); | ||
} | ||
if (missing.size) { | ||
this.state.error = 'Required: ' + [...missing.keys()].join(', '); | ||
return super.submit(); | ||
} | ||
let result = colors.unstyle(output); | ||
this.value = { values, result }; | ||
@@ -166,0 +183,0 @@ return super.submit(); |
@@ -25,2 +25,3 @@ 'use strict'; | ||
this.styles = prompt.styles; | ||
this.required = new Set(); | ||
this.cancelled = false; | ||
@@ -27,0 +28,0 @@ this.submitted = false; |
'use strict'; | ||
const utils = require('./utils'); | ||
const colors = require('ansi-colors'); | ||
const utils = require('./utils'); | ||
const styles = { | ||
default: colors.none, | ||
noop: colors.none, | ||
default: colors.noop, | ||
noop: colors.noop, | ||
@@ -44,2 +43,5 @@ /** | ||
get em() { | ||
return this.primary.italic; | ||
}, | ||
get heading() { | ||
return this.primary.underline; | ||
@@ -75,4 +77,22 @@ }, | ||
styles.merge = (options = {}) => { | ||
if (options.styles && typeof options.styles.enabled === 'boolean') { | ||
colors.enabled = options.styles.enabled; | ||
} | ||
if (options.styles && typeof options.styles.visible === 'boolean') { | ||
colors.visible = options.styles.visible; | ||
} | ||
let result = utils.merge({}, styles, options.styles); | ||
delete result.merge; | ||
for (let key of Object.keys(colors.styles)) { | ||
if (!result.hasOwnProperty(key)) { | ||
Reflect.defineProperty(result, key, { | ||
get() { | ||
return colors[key]; | ||
} | ||
}); | ||
} | ||
} | ||
return result; | ||
@@ -79,0 +99,0 @@ }; |
@@ -9,8 +9,11 @@ 'use strict'; | ||
super(options); | ||
this.maxChoices = options.maxChoices || Infinity; | ||
this.initial = options.initial || 0; | ||
this.longest = 0; | ||
this.cursorHide(); | ||
this.fns = []; | ||
} | ||
async initialize() { | ||
await this.reset(); | ||
await this.reset(true); | ||
await super.initialize(); | ||
@@ -23,2 +26,3 @@ } | ||
this.state.choices = []; | ||
this.choices = await Promise.all(await this.toChoices(choices)); | ||
@@ -38,15 +42,40 @@ this.choices.forEach(ch => (ch.enabled = false)); | ||
this.index = Math.max(0, Math.min(initial, this.choices.length)); | ||
this.enable(this.index); | ||
this.enable(this.find(this.index)); | ||
} | ||
} | ||
async toChoices(items) { | ||
if (typeof items === 'function') items = items.call(this); | ||
let list = await Promise.all(await items); | ||
return list.map(await this.toChoice.bind(this)); | ||
async toChoices(choices, parent) { | ||
if (typeof choices === 'function') choices = await choices.call(this); | ||
if (Array.isArray(choices)) { | ||
for (let i = 0; i < choices.length; i++) { | ||
if (typeof choices[i] === 'function') { | ||
choices[i] = await choices[i](this); | ||
} | ||
} | ||
} | ||
if (isObject(choices)) { | ||
choices = toArray(choices); | ||
} | ||
let list = await Promise.all(choices); | ||
for (let i = 0; i < list.length; i++) { | ||
let item = list[i]; | ||
item = await this.toChoice(item, i, parent); | ||
if (item.choices) { | ||
item.choices = await this.toChoices(item.choices, item); | ||
} | ||
list[i] = item; | ||
} | ||
return list; | ||
} | ||
async toChoice(ele, i) { | ||
async toChoice(ele, i, parent) { | ||
if (ele instanceof Promise) ele = await ele; | ||
if (typeof ele === 'function') ele = await ele.call(this); | ||
if (typeof ele === 'string') ele = { name: ele }; | ||
if (ele.normalized === true) return ele; | ||
ele.normalized = true; | ||
@@ -57,2 +86,3 @@ if (typeof ele.disabled === 'string') { | ||
} | ||
if (ele.disabled === true && !ele.hint) { | ||
@@ -66,4 +96,23 @@ ele.hint = '(disabled)'; | ||
ele.value = ele.value || ele.name; | ||
ele.cursor = 0; | ||
ele.input = ''; | ||
ele.index = i; | ||
ele.parent = parent; | ||
ele.indent = parent ? parent.indent + ' ' : (ele.indent || ''); | ||
ele.enabled = !!(this.multiple && !ele.disabled && (ele.enabled || this.isSelected(ele))); | ||
if (typeof ele.initial === 'function') { | ||
ele.initial = await ele.initial.call(this, this.state, ele, i); | ||
} | ||
this.longest = Math.max(this.longest, ele.message.length); | ||
let init = { ...ele }; | ||
ele.reset = (input = init.input, value = init.value) => { | ||
for (let key of Object.keys(init)) ele[key] = init[key]; | ||
ele.input = input; | ||
ele.value = value; | ||
}; | ||
return ele; | ||
@@ -74,3 +123,3 @@ } | ||
if (typeof choice.onChoice === 'function') { | ||
await choice.onChoice(this.state, choice, i); | ||
await choice.onChoice.call(this, this.state, choice, i); | ||
} | ||
@@ -80,11 +129,70 @@ } | ||
dispatch(s, key) { | ||
if (this.options.multiple && this[key.name]) { | ||
return this[key.name](); | ||
} | ||
this.alert(); | ||
} | ||
focus(choice, enabled) { | ||
if (typeof enabled !== 'boolean') enabled = choice.enabled; | ||
this.index = choice.index; | ||
choice.enabled = enabled && !this.skipChoice(choice); | ||
return choice; | ||
} | ||
space() { | ||
if (!this.options.multiple) return this.alert(); | ||
this.toggle(); | ||
this.toggle(this.focused); | ||
return this.render(); | ||
} | ||
a() { | ||
let choices = this.choices.filter(ch => !this.skipChoice(ch)); | ||
let enabled = choices.every(ch => ch.enabled); | ||
this.choices.forEach(ch => (ch.enabled = !enabled)); | ||
return this.render(); | ||
} | ||
i() { | ||
this.choices.forEach(ch => (ch.enabled = !ch.enabled)); | ||
return this.render(); | ||
} | ||
g(choice = this.focused) { | ||
if (!this.choices.some(ch => !!ch.parent)) return this.a(); | ||
this.toggle((choice.parent && !choice.choices) ? choice.parent : choice); | ||
return this.render(); | ||
} | ||
toggle(choice, enabled) { | ||
if (typeof enabled !== 'boolean') enabled = !choice.enabled; | ||
choice.enabled = enabled; | ||
if (choice.choices) { | ||
choice.choices.forEach(ch => this.toggle(ch, enabled)); | ||
} | ||
let parent = choice.parent; | ||
while (parent) { | ||
let choices = parent.choices.filter(ch => ch.disabled); | ||
parent.enabled = choices.every(ch => ch.enabled); | ||
parent = parent.parent; | ||
} | ||
reset(this, this.choices); | ||
return choice; | ||
} | ||
enable(choice) { | ||
choice.enabled = !this.skipChoice(choice); | ||
choice.choices && choice.choices.forEach(ch => this.enable(ch)); | ||
return choice; | ||
} | ||
disable(choice) { | ||
choice.enabled = false; | ||
choice.choices && choice.choices.forEach(this.disable.bind(this)); | ||
return choice; | ||
} | ||
number(n) { | ||
@@ -112,17 +220,6 @@ let i = Number(n); | ||
this.index = this.choices.indexOf(choice); | ||
this.toggle(); | ||
this.toggle(this.focused); | ||
return this.render(); | ||
} | ||
a() { | ||
let enabled = this.isEnabled(this); | ||
this.choices.forEach(choice => (choice.enabled = !enabled && !this.skipChoice(choice))); | ||
return this.render(); | ||
} | ||
i() { | ||
this.choices.forEach(this.toggle.bind(this)); | ||
return this.render(); | ||
} | ||
home() { | ||
@@ -266,34 +363,7 @@ this.choices = reorder(this.choices); | ||
focus(choice, enabled) { | ||
choice = this.find(choice); | ||
if (typeof enabled !== 'boolean') enabled = choice.enabled; | ||
this.index = choice.index; | ||
choice.enabled = enabled && !this.skipChoice(choice); | ||
} | ||
toggle(choice = this.focused) { | ||
choice = this.find(choice); | ||
choice.enabled = !choice.enabled && !this.skipChoice(choice); | ||
choice.choices && choice.choices.forEach(this.toggle.bind(this)); | ||
return choice; | ||
} | ||
enable(choice = this.focused) { | ||
choice = this.find(choice); | ||
choice.enabled = !this.skipChoice(choice); | ||
choice.choices && choice.choices.forEach(this.enable.bind(this)); | ||
return choice; | ||
} | ||
disable(choice = this.focused) { | ||
choice = this.find(choice); | ||
choice.enabled = false; | ||
choice.choices && choice.choices.forEach(this.disable.bind(this)); | ||
return choice; | ||
} | ||
isEnabled(choice = this.focused) { | ||
if (Array.isArray(choice)) return choice.every(ch => this.isEnabled(ch)); | ||
if (choice.choices) { | ||
let choices = choice.choices.filter(ch => !ch.disabled); | ||
return choices.every(this.isEnabled.bind(this)); | ||
let choices = choice.choices.filter(ch => !this.skipChoice(ch)); | ||
return choice.enabled && choices.every(ch => this.isEnabled(ch)); | ||
} | ||
@@ -345,3 +415,3 @@ return choice.enabled && !this.skipChoice(choice); | ||
async submit(value) { | ||
async submit() { | ||
if (this.options.reorder !== false && this.options.sort !== true) { | ||
@@ -371,4 +441,6 @@ this.choices = reorder(this.choices); | ||
let choice = this.find(init); | ||
this.initial = choice.index; | ||
this.focus(choice, true); | ||
if (choice) { | ||
this.initial = choice.index; | ||
this.focus(choice, true); | ||
} | ||
} | ||
@@ -378,3 +450,3 @@ } | ||
get choices() { | ||
return this.state.choices || []; | ||
return reset(this, this.state.choices || []); | ||
} | ||
@@ -405,3 +477,3 @@ | ||
get enabled() { | ||
return this.filter(choice => !this.skipChoice(choice) && choice.enabled); | ||
return this.filter(choice => this.isEnabled(choice)); | ||
} | ||
@@ -422,2 +494,75 @@ | ||
function toArray(choices) { | ||
let i = 0; | ||
const toChoices = (list, parent) => { | ||
if (Array.isArray(list)) { | ||
return list.map(ch => toChoice(ch, i++, parent)); | ||
} | ||
let result = []; | ||
for (let name of Object.keys(list)) { | ||
if (typeof list[name] === 'string') { | ||
result.push(list); | ||
continue; | ||
} | ||
let ele = list[name]; | ||
if (isObject(ele) && !hasChoices(ele)) { | ||
result.push(toChoice({ name, ...ele }, i++, parent)); | ||
continue; | ||
} | ||
let group = toChoice(name, i++, parent); | ||
result.push(group); | ||
if (ele && typeof ele === 'object') { | ||
group.choices = toChoices(ele, group); | ||
group.choices.forEach(c => result.push(c)); | ||
} else { | ||
group[name] = ele; | ||
} | ||
} | ||
return result; | ||
}; | ||
return toChoices(choices); | ||
} | ||
function reset(prompt, choices) { | ||
for (let choice of choices) { | ||
if (choice.choices) { | ||
let items = choice.choices.filter(ch => !prompt.skipChoice(ch)); | ||
choice.enabled = items.every(ch => ch.enabled === true); | ||
} | ||
if (prompt.skipChoice(choice) === true) { | ||
choice.enabled = false; | ||
} | ||
} | ||
return choices; | ||
} | ||
function hasChoices(obj) { | ||
let hasArray = false; | ||
let i = 0; | ||
for (let key of Object.keys(obj)) { | ||
i++; | ||
if (Array.isArray(obj[key])) { | ||
hasArray = true; | ||
break; | ||
} | ||
if (isObject(obj[key])) { | ||
hasArray = hasChoices(obj[key]); | ||
break; | ||
} | ||
hasArray = false; | ||
} | ||
return i === 1 && hasArray; | ||
} | ||
function toChoice(choice, i, parent) { | ||
if (typeof choice === 'string') choice = { name: choice, message: choice }; | ||
choice.index = i; | ||
choice.parent = parent; | ||
choice.indent = parent ? parent.indent + ' ' : (choice.indent || ''); | ||
return choice; | ||
} | ||
module.exports = ArrayPrompt; |
@@ -46,15 +46,16 @@ 'use strict'; | ||
let message = await this.message(); | ||
let prompt = this.state.prompt = [prefix, message, separator].join(' '); | ||
let promptLine = this.state.prompt = [prefix, message, separator].join(' '); | ||
let header = await this.header(); | ||
let footer = await this.footer(); | ||
let value = this.value = this.cast(input); | ||
let output = await this.format(value); | ||
let help = await this.error() || await this.hint(); | ||
let footer = await this.footer(); | ||
if (output) prompt += ' ' + output; | ||
if (help) prompt += ' ' + help; | ||
if (output || !help) promptLine += ' ' + output; | ||
if (help && !promptLine.includes(help)) promptLine += ' ' + help; | ||
this.clear(size); | ||
this.write([header, prompt, help, footer].filter(Boolean).join('\n')); | ||
this.write([header, promptLine, footer].filter(Boolean).join('\n')); | ||
this.restore(); | ||
@@ -61,0 +62,0 @@ } |
@@ -17,26 +17,20 @@ 'use strict'; | ||
this.value = this.input = String(this.initial); | ||
this.cursor = this.input.length; | ||
this.cursorShow(); | ||
} | ||
dispatch(ch) { | ||
append(ch) { | ||
if (!/[-+.]/.test(ch) || (ch === '.' && this.input.includes('.'))) { | ||
return this.alert('invalid number'); | ||
} | ||
return this.append(ch); | ||
return super.append(ch); | ||
} | ||
number(ch) { | ||
return this.append(ch); | ||
return super.append(ch); | ||
} | ||
delete() { | ||
super.delete(); | ||
if (this.input === '' && this.initial != null) { | ||
this.input = String(this.initial); | ||
return this.render(); | ||
} | ||
} | ||
next() { | ||
if (!this.initial) return this.alert(); | ||
if (this.input && this.input !== this.initial) return this.alert(); | ||
if (!this.isValue(this.initial)) return this.alert(); | ||
this.input = this.initial; | ||
@@ -75,3 +69,3 @@ this.cursor = String(this.initial).length; | ||
} | ||
return this.styles.em(input); | ||
return this.styles.info(input); | ||
} | ||
@@ -78,0 +72,0 @@ |
'use strict'; | ||
const Prompt = require('../prompt'); | ||
const placeholder = require('../placeholder'); | ||
const Prompt = require('../prompt'); | ||
const { isPrimitive } = require('../utils'); | ||
@@ -46,2 +46,9 @@ | ||
deleteForward() { | ||
let { cursor, input } = this.state; | ||
if (input[cursor] === void 0) return this.alert(); | ||
this.input = `${input}`.slice(0, cursor) + `${input}`.slice(cursor + 1); | ||
this.render(); | ||
} | ||
first() { | ||
@@ -69,3 +76,4 @@ this.cursor = 0; | ||
next() { | ||
if (!this.initial || !this.initial.startsWith(this.input)) return this.alert(); | ||
let init = this.initial != null ? String(this.initial) : ''; | ||
if (!init || !init.startsWith(this.input)) return this.alert(); | ||
this.input = this.initial; | ||
@@ -84,3 +92,3 @@ this.cursor = this.initial.length; | ||
this.moveCursor(1); | ||
this.render(); | ||
return this.render(); | ||
} | ||
@@ -91,3 +99,3 @@ | ||
this.moveCursor(-1); | ||
this.render(); | ||
return this.render(); | ||
} | ||
@@ -121,4 +129,4 @@ | ||
if (output || !help) prompt += ' ' + output; | ||
if (help && !prompt.includes(help)) prompt += ' ' + help; | ||
if (help && !output.includes(help)) output += ' ' + help; | ||
prompt += ' ' + output; | ||
@@ -125,0 +133,0 @@ this.clear(size); |
@@ -239,8 +239,2 @@ 'use strict'; | ||
exports.timeout = (fn, ms = 0) => { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => fn().then(resolve).catch(reject), ms); | ||
}); | ||
}; | ||
exports.define = (obj, key, value) => { | ||
@@ -247,0 +241,0 @@ Reflect.defineProperty(obj, key, { value }); |
{ | ||
"name": "enquirer", | ||
"description": "Stylish, intuitive and user-friendly prompt system. Fast and lightweight enough for small projects, powerful and extensible enough for the most advanced use cases.", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"homepage": "https://github.com/enquirer/enquirer", | ||
@@ -6,0 +6,0 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)", |
@@ -42,3 +42,3 @@ # enquirer [![NPM version](https://img.shields.io/npm/v/enquirer.svg?style=flat)](https://www.npmjs.com/package/enquirer) [![NPM monthly downloads](https://img.shields.io/npm/dm/enquirer.svg?style=flat)](https://npmjs.org/package/enquirer) [![NPM total downloads](https://img.shields.io/npm/dt/enquirer.svg?style=flat)](https://npmjs.org/package/enquirer) [![Linux Build Status](https://img.shields.io/travis/enquirer/enquirer.svg?style=flat&label=Travis)](https://travis-ci.org/enquirer/enquirer) | ||
This documentation is for the beta, which means we're still working on documenting everything, creating [examples](./examples), and having fun writing [recipes](./recipes) to demonstrate how [prompts](#prompt-api) can be extended and customized. If you'd like to contribute to any of these, please feel free to open an [issue](./issues/new) for discussion or [pull request](./pulls) with your changes. We are also looking for technical writers to help with writing more detailed user and developer documentation, tutorials, and blog posts about Enquirer 2 and all of the awesome features that are available. | ||
This documentation is for the beta, which means we're still working on documenting everything, creating [examples](./examples), and having fun writing [recipes](./recipes) to demonstrate how [prompts](#prompt-api) can be extended and customized. If you'd like to contribute to any of these, please feel free to open an [issue](https://github.com/enquirer/enquirer/issues/new) for discussion or [pull request](https://github.com/enquirer/enquirer/pulls) with your changes. We are also looking for technical writers to help with writing more detailed user and developer documentation, tutorials, and blog posts about Enquirer 2 and all of the awesome features that are available. | ||
@@ -185,3 +185,3 @@ <br> | ||
### [use](index.js#L162) | ||
### [use](index.js#L152) | ||
@@ -206,3 +206,3 @@ Use an enquirer plugin. | ||
### [use](index.js#L184) | ||
### [use](index.js#L174) | ||
@@ -225,3 +225,3 @@ Programmatically cancel all prompts. | ||
### [Enquirer#prompt](index.js#L242) | ||
### [Enquirer#prompt](index.js#L232) | ||
@@ -403,2 +403,2 @@ Prompt function that takes a "question" object or array of question objects, and returns an object with responses from the user. | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on October 30, 2018._ | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on November 04, 2018._ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
103442
2901
0