| 'use strict'; |
| |
| const toString = Object.prototype.toString; |
| const colors = require('ansi-colors'); |
| let called = false; |
| let fns = []; |
| |
| const complements = { |
| 'yellow': 'blue', |
| 'cyan': 'red', |
| 'green': 'magenta', |
| 'black': 'white', |
| 'blue': 'yellow', |
| 'red': 'cyan', |
| 'magenta': 'green', |
| 'white': 'black' |
| }; |
| |
| exports.longest = (arr, prop) => { |
| return arr.reduce((a, v) => Math.max(a, prop ? v[prop].length : v.length), 0); |
| }; |
| |
| exports.hasColor = str => !!str && colors.hasColor(str); |
| |
| const isObject = exports.isObject = val => { |
| return val !== null && typeof val === 'object' && !Array.isArray(val); |
| }; |
| |
| exports.nativeType = val => { |
| return toString.call(val).slice(8, -1).toLowerCase().replace(/\s/g, ''); |
| }; |
| |
| exports.isAsyncFn = val => { |
| return exports.nativeType(val) === 'asyncfunction'; |
| }; |
| |
| exports.isPrimitive = val => { |
| return val != null && typeof val !== 'object' && typeof val !== 'function'; |
| }; |
| |
| exports.resolve = (context, value, ...rest) => { |
| if (typeof value === 'function') { |
| return value.call(context, ...rest); |
| } |
| return value; |
| }; |
| |
| exports.scrollDown = (choices = []) => [...choices.slice(1), choices[0]]; |
| exports.scrollUp = (choices = []) => [choices.pop(), ...choices]; |
| |
| exports.reorder = (arr = []) => { |
| let res = arr.slice(); |
| res.sort((a, b) => { |
| if (a.index > b.index) return 1; |
| if (a.index < b.index) return -1; |
| return 0; |
| }); |
| return res; |
| }; |
| |
| exports.swap = (arr, index, pos) => { |
| let len = arr.length; |
| let idx = pos === len ? 0 : pos < 0 ? len - 1 : pos; |
| let choice = arr[index]; |
| arr[index] = arr[idx]; |
| arr[idx] = choice; |
| }; |
| |
| exports.width = (stream, fallback = 80) => { |
| let columns = (stream && stream.columns) ? stream.columns : fallback; |
| if (stream && typeof stream.getWindowSize === 'function') { |
| columns = stream.getWindowSize()[0]; |
| } |
| if (process.platform === 'win32') { |
| return columns - 1; |
| } |
| return columns; |
| }; |
| |
| exports.height = (stream, fallback = 20) => { |
| let rows = (stream && stream.rows) ? stream.rows : fallback; |
| if (stream && typeof stream.getWindowSize === 'function') { |
| rows = stream.getWindowSize()[1]; |
| } |
| return rows; |
| }; |
| |
| exports.wordWrap = (str, options = {}) => { |
| if (!str) return str; |
| |
| if (typeof options === 'number') { |
| options = { width: options }; |
| } |
| |
| let { indent = '', newline = ('\n' + indent), width = 80 } = options; |
| let spaces = (newline + indent).match(/[^\S\n]/g) || []; |
| width -= spaces.length; |
| let source = `.{1,${width}}([\\s\\u200B]+|$)|[^\\s\\u200B]+?([\\s\\u200B]+|$)`; |
| let output = str.trim(); |
| let regex = new RegExp(source, 'g'); |
| let lines = output.match(regex) || []; |
| lines = lines.map(line => line.replace(/\n$/, '')); |
| if (options.padEnd) lines = lines.map(line => line.padEnd(width, ' ')); |
| if (options.padStart) lines = lines.map(line => line.padStart(width, ' ')); |
| return indent + lines.join(newline); |
| }; |
| |
| exports.unmute = color => { |
| let name = color.stack.find(n => colors.keys.color.includes(n)); |
| if (name) { |
| return colors[name]; |
| } |
| let bg = color.stack.find(n => n.slice(2) === 'bg'); |
| if (bg) { |
| return colors[name.slice(2)]; |
| } |
| return str => str; |
| }; |
| |
| exports.pascal = str => str ? str[0].toUpperCase() + str.slice(1) : ''; |
| |
| exports.inverse = color => { |
| if (!color || !color.stack) return color; |
| let name = color.stack.find(n => colors.keys.color.includes(n)); |
| if (name) { |
| let col = colors['bg' + exports.pascal(name)]; |
| return col ? col.black : color; |
| } |
| let bg = color.stack.find(n => n.slice(0, 2) === 'bg'); |
| if (bg) { |
| return colors[bg.slice(2).toLowerCase()] || color; |
| } |
| return colors.none; |
| }; |
| |
| exports.complement = color => { |
| if (!color || !color.stack) return color; |
| let name = color.stack.find(n => colors.keys.color.includes(n)); |
| let bg = color.stack.find(n => n.slice(0, 2) === 'bg'); |
| if (name && !bg) { |
| return colors[complements[name] || name]; |
| } |
| if (bg) { |
| let lower = bg.slice(2).toLowerCase(); |
| let comp = complements[lower]; |
| if (!comp) return color; |
| return colors['bg' + exports.pascal(comp)] || color; |
| } |
| return colors.none; |
| }; |
| |
| exports.meridiem = date => { |
| let hours = date.getHours(); |
| let minutes = date.getMinutes(); |
| let ampm = hours >= 12 ? 'pm' : 'am'; |
| hours = hours % 12; |
| let hrs = hours === 0 ? 12 : hours; |
| let min = minutes < 10 ? '0' + minutes : minutes; |
| return hrs + ':' + min + ' ' + ampm; |
| }; |
| |
| /** |
| * Set a value on the given object. |
| * @param {Object} obj |
| * @param {String} prop |
| * @param {any} value |
| */ |
| |
| exports.set = (obj = {}, prop = '', val) => { |
| return prop.split('.').reduce((acc, k, i, arr) => { |
| let value = arr.length - 1 > i ? (acc[k] || {}) : val; |
| if (!exports.isObject(value) && i < arr.length - 1) value = {}; |
| return (acc[k] = value); |
| }, obj); |
| }; |
| |
| /** |
| * Get a value from the given object. |
| * @param {Object} obj |
| * @param {String} prop |
| */ |
| |
| exports.get = (obj = {}, prop = '', fallback) => { |
| let value = obj[prop] == null |
| ? prop.split('.').reduce((acc, k) => acc && acc[k], obj) |
| : obj[prop]; |
| return value == null ? fallback : value; |
| }; |
| |
| exports.mixin = (target, b) => { |
| if (!isObject(target)) return b; |
| if (!isObject(b)) return target; |
| for (let key of Object.keys(b)) { |
| let desc = Object.getOwnPropertyDescriptor(b, key); |
| if (desc.hasOwnProperty('value')) { |
| if (target.hasOwnProperty(key) && isObject(desc.value)) { |
| let existing = Object.getOwnPropertyDescriptor(target, key); |
| if (isObject(existing.value)) { |
| target[key] = exports.merge({}, target[key], b[key]); |
| } else { |
| Reflect.defineProperty(target, key, desc); |
| } |
| } else { |
| Reflect.defineProperty(target, key, desc); |
| } |
| } else { |
| Reflect.defineProperty(target, key, desc); |
| } |
| } |
| return target; |
| }; |
| |
| exports.merge = (...args) => { |
| let target = {}; |
| for (let ele of args) exports.mixin(target, ele); |
| return target; |
| }; |
| |
| exports.mixinEmitter = (obj, emitter) => { |
| let proto = emitter.constructor.prototype; |
| for (let key of Object.keys(proto)) { |
| let val = proto[key]; |
| if (typeof val === 'function') { |
| exports.define(obj, key, val.bind(emitter)); |
| } else { |
| exports.define(obj, key, val); |
| } |
| } |
| }; |
| |
| exports.onExit = callback => { |
| const onExit = (quit, code) => { |
| if (called) return; |
| |
| called = true; |
| fns.forEach(fn => fn()); |
| |
| if (quit === true) { |
| process.exit(128 + code); |
| } |
| }; |
| |
| if (fns.length === 0) { |
| process.once('SIGTERM', onExit.bind(null, true, 15)); |
| process.once('SIGINT', onExit.bind(null, true, 2)); |
| process.once('exit', onExit); |
| } |
| |
| fns.push(callback); |
| }; |
| |
| exports.define = (obj, key, value) => { |
| Reflect.defineProperty(obj, key, { value }); |
| }; |
| |
| exports.defineExport = (obj, key, fn) => { |
| let custom; |
| Reflect.defineProperty(obj, key, { |
| enumerable: true, |
| configurable: true, |
| set(val) { |
| custom = val; |
| }, |
| get() { |
| return custom ? custom() : fn(); |
| } |
| }); |
| }; |