You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
6.8 KiB

4 years ago
  1. 'use strict';
  2. const toString = Object.prototype.toString;
  3. const colors = require('ansi-colors');
  4. let called = false;
  5. let fns = [];
  6. const complements = {
  7. 'yellow': 'blue',
  8. 'cyan': 'red',
  9. 'green': 'magenta',
  10. 'black': 'white',
  11. 'blue': 'yellow',
  12. 'red': 'cyan',
  13. 'magenta': 'green',
  14. 'white': 'black'
  15. };
  16. exports.longest = (arr, prop) => {
  17. return arr.reduce((a, v) => Math.max(a, prop ? v[prop].length : v.length), 0);
  18. };
  19. exports.hasColor = str => !!str && colors.hasColor(str);
  20. const isObject = exports.isObject = val => {
  21. return val !== null && typeof val === 'object' && !Array.isArray(val);
  22. };
  23. exports.nativeType = val => {
  24. return toString.call(val).slice(8, -1).toLowerCase().replace(/\s/g, '');
  25. };
  26. exports.isAsyncFn = val => {
  27. return exports.nativeType(val) === 'asyncfunction';
  28. };
  29. exports.isPrimitive = val => {
  30. return val != null && typeof val !== 'object' && typeof val !== 'function';
  31. };
  32. exports.resolve = (context, value, ...rest) => {
  33. if (typeof value === 'function') {
  34. return value.call(context, ...rest);
  35. }
  36. return value;
  37. };
  38. exports.scrollDown = (choices = []) => [...choices.slice(1), choices[0]];
  39. exports.scrollUp = (choices = []) => [choices.pop(), ...choices];
  40. exports.reorder = (arr = []) => {
  41. let res = arr.slice();
  42. res.sort((a, b) => {
  43. if (a.index > b.index) return 1;
  44. if (a.index < b.index) return -1;
  45. return 0;
  46. });
  47. return res;
  48. };
  49. exports.swap = (arr, index, pos) => {
  50. let len = arr.length;
  51. let idx = pos === len ? 0 : pos < 0 ? len - 1 : pos;
  52. let choice = arr[index];
  53. arr[index] = arr[idx];
  54. arr[idx] = choice;
  55. };
  56. exports.width = (stream, fallback = 80) => {
  57. let columns = (stream && stream.columns) ? stream.columns : fallback;
  58. if (stream && typeof stream.getWindowSize === 'function') {
  59. columns = stream.getWindowSize()[0];
  60. }
  61. if (process.platform === 'win32') {
  62. return columns - 1;
  63. }
  64. return columns;
  65. };
  66. exports.height = (stream, fallback = 20) => {
  67. let rows = (stream && stream.rows) ? stream.rows : fallback;
  68. if (stream && typeof stream.getWindowSize === 'function') {
  69. rows = stream.getWindowSize()[1];
  70. }
  71. return rows;
  72. };
  73. exports.wordWrap = (str, options = {}) => {
  74. if (!str) return str;
  75. if (typeof options === 'number') {
  76. options = { width: options };
  77. }
  78. let { indent = '', newline = ('\n' + indent), width = 80 } = options;
  79. let spaces = (newline + indent).match(/[^\S\n]/g) || [];
  80. width -= spaces.length;
  81. let source = `.{1,${width}}([\\s\\u200B]+|$)|[^\\s\\u200B]+?([\\s\\u200B]+|$)`;
  82. let output = str.trim();
  83. let regex = new RegExp(source, 'g');
  84. let lines = output.match(regex) || [];
  85. lines = lines.map(line => line.replace(/\n$/, ''));
  86. if (options.padEnd) lines = lines.map(line => line.padEnd(width, ' '));
  87. if (options.padStart) lines = lines.map(line => line.padStart(width, ' '));
  88. return indent + lines.join(newline);
  89. };
  90. exports.unmute = color => {
  91. let name = color.stack.find(n => colors.keys.color.includes(n));
  92. if (name) {
  93. return colors[name];
  94. }
  95. let bg = color.stack.find(n => n.slice(2) === 'bg');
  96. if (bg) {
  97. return colors[name.slice(2)];
  98. }
  99. return str => str;
  100. };
  101. exports.pascal = str => str ? str[0].toUpperCase() + str.slice(1) : '';
  102. exports.inverse = color => {
  103. if (!color || !color.stack) return color;
  104. let name = color.stack.find(n => colors.keys.color.includes(n));
  105. if (name) {
  106. let col = colors['bg' + exports.pascal(name)];
  107. return col ? col.black : color;
  108. }
  109. let bg = color.stack.find(n => n.slice(0, 2) === 'bg');
  110. if (bg) {
  111. return colors[bg.slice(2).toLowerCase()] || color;
  112. }
  113. return colors.none;
  114. };
  115. exports.complement = color => {
  116. if (!color || !color.stack) return color;
  117. let name = color.stack.find(n => colors.keys.color.includes(n));
  118. let bg = color.stack.find(n => n.slice(0, 2) === 'bg');
  119. if (name && !bg) {
  120. return colors[complements[name] || name];
  121. }
  122. if (bg) {
  123. let lower = bg.slice(2).toLowerCase();
  124. let comp = complements[lower];
  125. if (!comp) return color;
  126. return colors['bg' + exports.pascal(comp)] || color;
  127. }
  128. return colors.none;
  129. };
  130. exports.meridiem = date => {
  131. let hours = date.getHours();
  132. let minutes = date.getMinutes();
  133. let ampm = hours >= 12 ? 'pm' : 'am';
  134. hours = hours % 12;
  135. let hrs = hours === 0 ? 12 : hours;
  136. let min = minutes < 10 ? '0' + minutes : minutes;
  137. return hrs + ':' + min + ' ' + ampm;
  138. };
  139. /**
  140. * Set a value on the given object.
  141. * @param {Object} obj
  142. * @param {String} prop
  143. * @param {any} value
  144. */
  145. exports.set = (obj = {}, prop = '', val) => {
  146. return prop.split('.').reduce((acc, k, i, arr) => {
  147. let value = arr.length - 1 > i ? (acc[k] || {}) : val;
  148. if (!exports.isObject(value) && i < arr.length - 1) value = {};
  149. return (acc[k] = value);
  150. }, obj);
  151. };
  152. /**
  153. * Get a value from the given object.
  154. * @param {Object} obj
  155. * @param {String} prop
  156. */
  157. exports.get = (obj = {}, prop = '', fallback) => {
  158. let value = obj[prop] == null
  159. ? prop.split('.').reduce((acc, k) => acc && acc[k], obj)
  160. : obj[prop];
  161. return value == null ? fallback : value;
  162. };
  163. exports.mixin = (target, b) => {
  164. if (!isObject(target)) return b;
  165. if (!isObject(b)) return target;
  166. for (let key of Object.keys(b)) {
  167. let desc = Object.getOwnPropertyDescriptor(b, key);
  168. if (desc.hasOwnProperty('value')) {
  169. if (target.hasOwnProperty(key) && isObject(desc.value)) {
  170. let existing = Object.getOwnPropertyDescriptor(target, key);
  171. if (isObject(existing.value)) {
  172. target[key] = exports.merge({}, target[key], b[key]);
  173. } else {
  174. Reflect.defineProperty(target, key, desc);
  175. }
  176. } else {
  177. Reflect.defineProperty(target, key, desc);
  178. }
  179. } else {
  180. Reflect.defineProperty(target, key, desc);
  181. }
  182. }
  183. return target;
  184. };
  185. exports.merge = (...args) => {
  186. let target = {};
  187. for (let ele of args) exports.mixin(target, ele);
  188. return target;
  189. };
  190. exports.mixinEmitter = (obj, emitter) => {
  191. let proto = emitter.constructor.prototype;
  192. for (let key of Object.keys(proto)) {
  193. let val = proto[key];
  194. if (typeof val === 'function') {
  195. exports.define(obj, key, val.bind(emitter));
  196. } else {
  197. exports.define(obj, key, val);
  198. }
  199. }
  200. };
  201. exports.onExit = callback => {
  202. const onExit = (quit, code) => {
  203. if (called) return;
  204. called = true;
  205. fns.forEach(fn => fn());
  206. if (quit === true) {
  207. process.exit(128 + code);
  208. }
  209. };
  210. if (fns.length === 0) {
  211. process.once('SIGTERM', onExit.bind(null, true, 15));
  212. process.once('SIGINT', onExit.bind(null, true, 2));
  213. process.once('exit', onExit);
  214. }
  215. fns.push(callback);
  216. };
  217. exports.define = (obj, key, value) => {
  218. Reflect.defineProperty(obj, key, { value });
  219. };
  220. exports.defineExport = (obj, key, fn) => {
  221. let custom;
  222. Reflect.defineProperty(obj, key, {
  223. enumerable: true,
  224. configurable: true,
  225. set(val) {
  226. custom = val;
  227. },
  228. get() {
  229. return custom ? custom() : fn();
  230. }
  231. });
  232. };