3 const toString = Object.prototype.toString;
4 const colors = require('ansi-colors');
19 exports.longest = (arr, prop) => {
20 return arr.reduce((a, v) => Math.max(a, prop ? v[prop].length : v.length), 0);
23 exports.hasColor = str => !!str && colors.hasColor(str);
25 const isObject = exports.isObject = val => {
26 return val !== null && typeof val === 'object' && !Array.isArray(val);
29 exports.nativeType = val => {
30 return toString.call(val).slice(8, -1).toLowerCase().replace(/\s/g, '');
33 exports.isAsyncFn = val => {
34 return exports.nativeType(val) === 'asyncfunction';
37 exports.isPrimitive = val => {
38 return val != null && typeof val !== 'object' && typeof val !== 'function';
41 exports.resolve = (context, value, ...rest) => {
42 if (typeof value === 'function') {
43 return value.call(context, ...rest);
48 exports.scrollDown = (choices = []) => [...choices.slice(1), choices[0]];
49 exports.scrollUp = (choices = []) => [choices.pop(), ...choices];
51 exports.reorder = (arr = []) => {
52 let res = arr.slice();
54 if (a.index > b.index) return 1;
55 if (a.index < b.index) return -1;
61 exports.swap = (arr, index, pos) => {
63 let idx = pos === len ? 0 : pos < 0 ? len - 1 : pos;
64 let choice = arr[index];
65 arr[index] = arr[idx];
69 exports.width = (stream, fallback = 80) => {
70 let columns = (stream && stream.columns) ? stream.columns : fallback;
71 if (stream && typeof stream.getWindowSize === 'function') {
72 columns = stream.getWindowSize()[0];
74 if (process.platform === 'win32') {
80 exports.height = (stream, fallback = 20) => {
81 let rows = (stream && stream.rows) ? stream.rows : fallback;
82 if (stream && typeof stream.getWindowSize === 'function') {
83 rows = stream.getWindowSize()[1];
88 exports.wordWrap = (str, options = {}) => {
91 if (typeof options === 'number') {
92 options = { width: options };
95 let { indent = '', newline = ('\n' + indent), width = 80 } = options;
96 let spaces = (newline + indent).match(/[^\S\n]/g) || [];
97 width -= spaces.length;
98 let source = `.{1,${width}}([\\s\\u200B]+|$)|[^\\s\\u200B]+?([\\s\\u200B]+|$)`;
99 let output = str.trim();
100 let regex = new RegExp(source, 'g');
101 let lines = output.match(regex) || [];
102 lines = lines.map(line => line.replace(/\n$/, ''));
103 if (options.padEnd) lines = lines.map(line => line.padEnd(width, ' '));
104 if (options.padStart) lines = lines.map(line => line.padStart(width, ' '));
105 return indent + lines.join(newline);
108 exports.unmute = color => {
109 let name = color.stack.find(n => colors.keys.color.includes(n));
113 let bg = color.stack.find(n => n.slice(2) === 'bg');
115 return colors[name.slice(2)];
120 exports.pascal = str => str ? str[0].toUpperCase() + str.slice(1) : '';
122 exports.inverse = color => {
123 if (!color || !color.stack) return color;
124 let name = color.stack.find(n => colors.keys.color.includes(n));
126 let col = colors['bg' + exports.pascal(name)];
127 return col ? col.black : color;
129 let bg = color.stack.find(n => n.slice(0, 2) === 'bg');
131 return colors[bg.slice(2).toLowerCase()] || color;
136 exports.complement = color => {
137 if (!color || !color.stack) return color;
138 let name = color.stack.find(n => colors.keys.color.includes(n));
139 let bg = color.stack.find(n => n.slice(0, 2) === 'bg');
141 return colors[complements[name] || name];
144 let lower = bg.slice(2).toLowerCase();
145 let comp = complements[lower];
146 if (!comp) return color;
147 return colors['bg' + exports.pascal(comp)] || color;
152 exports.meridiem = date => {
153 let hours = date.getHours();
154 let minutes = date.getMinutes();
155 let ampm = hours >= 12 ? 'pm' : 'am';
157 let hrs = hours === 0 ? 12 : hours;
158 let min = minutes < 10 ? '0' + minutes : minutes;
159 return hrs + ':' + min + ' ' + ampm;
163 * Set a value on the given object.
164 * @param {Object} obj
165 * @param {String} prop
169 exports.set = (obj = {}, prop = '', val) => {
170 return prop.split('.').reduce((acc, k, i, arr) => {
171 let value = arr.length - 1 > i ? (acc[k] || {}) : val;
172 if (!exports.isObject(value) && i < arr.length - 1) value = {};
173 return (acc[k] = value);
178 * Get a value from the given object.
179 * @param {Object} obj
180 * @param {String} prop
183 exports.get = (obj = {}, prop = '', fallback) => {
184 let value = obj[prop] == null
185 ? prop.split('.').reduce((acc, k) => acc && acc[k], obj)
187 return value == null ? fallback : value;
190 exports.mixin = (target, b) => {
191 if (!isObject(target)) return b;
192 if (!isObject(b)) return target;
193 for (let key of Object.keys(b)) {
194 let desc = Object.getOwnPropertyDescriptor(b, key);
195 if (desc.hasOwnProperty('value')) {
196 if (target.hasOwnProperty(key) && isObject(desc.value)) {
197 let existing = Object.getOwnPropertyDescriptor(target, key);
198 if (isObject(existing.value)) {
199 target[key] = exports.merge({}, target[key], b[key]);
201 Reflect.defineProperty(target, key, desc);
204 Reflect.defineProperty(target, key, desc);
207 Reflect.defineProperty(target, key, desc);
213 exports.merge = (...args) => {
215 for (let ele of args) exports.mixin(target, ele);
219 exports.mixinEmitter = (obj, emitter) => {
220 let proto = emitter.constructor.prototype;
221 for (let key of Object.keys(proto)) {
222 let val = proto[key];
223 if (typeof val === 'function') {
224 exports.define(obj, key, val.bind(emitter));
226 exports.define(obj, key, val);
231 exports.onExit = callback => {
232 const onExit = (quit, code) => {
236 fns.forEach(fn => fn());
239 process.exit(128 + code);
243 if (fns.length === 0) {
244 process.once('SIGTERM', onExit.bind(null, true, 15));
245 process.once('SIGINT', onExit.bind(null, true, 2));
246 process.once('exit', onExit);
252 exports.define = (obj, key, value) => {
253 Reflect.defineProperty(obj, key, { value });
256 exports.defineExport = (obj, key, fn) => {
258 Reflect.defineProperty(obj, key, {
265 return custom ? custom() : fn();