core.mjs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. const LogLevels = {
  2. silent: Number.NEGATIVE_INFINITY,
  3. fatal: 0,
  4. error: 0,
  5. warn: 1,
  6. log: 2,
  7. info: 3,
  8. success: 3,
  9. fail: 3,
  10. ready: 3,
  11. start: 3,
  12. box: 3,
  13. debug: 4,
  14. trace: 5,
  15. verbose: Number.POSITIVE_INFINITY
  16. };
  17. const LogTypes = {
  18. // Silent
  19. silent: {
  20. level: -1
  21. },
  22. // Level 0
  23. fatal: {
  24. level: LogLevels.fatal
  25. },
  26. error: {
  27. level: LogLevels.error
  28. },
  29. // Level 1
  30. warn: {
  31. level: LogLevels.warn
  32. },
  33. // Level 2
  34. log: {
  35. level: LogLevels.log
  36. },
  37. // Level 3
  38. info: {
  39. level: LogLevels.info
  40. },
  41. success: {
  42. level: LogLevels.success
  43. },
  44. fail: {
  45. level: LogLevels.fail
  46. },
  47. ready: {
  48. level: LogLevels.info
  49. },
  50. start: {
  51. level: LogLevels.info
  52. },
  53. box: {
  54. level: LogLevels.info
  55. },
  56. // Level 4
  57. debug: {
  58. level: LogLevels.debug
  59. },
  60. // Level 5
  61. trace: {
  62. level: LogLevels.trace
  63. },
  64. // Verbose
  65. verbose: {
  66. level: LogLevels.verbose
  67. }
  68. };
  69. function isPlainObject$1(value) {
  70. if (value === null || typeof value !== "object") {
  71. return false;
  72. }
  73. const prototype = Object.getPrototypeOf(value);
  74. if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
  75. return false;
  76. }
  77. if (Symbol.iterator in value) {
  78. return false;
  79. }
  80. if (Symbol.toStringTag in value) {
  81. return Object.prototype.toString.call(value) === "[object Module]";
  82. }
  83. return true;
  84. }
  85. function _defu(baseObject, defaults, namespace = ".", merger) {
  86. if (!isPlainObject$1(defaults)) {
  87. return _defu(baseObject, {}, namespace, merger);
  88. }
  89. const object = Object.assign({}, defaults);
  90. for (const key in baseObject) {
  91. if (key === "__proto__" || key === "constructor") {
  92. continue;
  93. }
  94. const value = baseObject[key];
  95. if (value === null || value === void 0) {
  96. continue;
  97. }
  98. if (merger && merger(object, key, value, namespace)) {
  99. continue;
  100. }
  101. if (Array.isArray(value) && Array.isArray(object[key])) {
  102. object[key] = [...value, ...object[key]];
  103. } else if (isPlainObject$1(value) && isPlainObject$1(object[key])) {
  104. object[key] = _defu(
  105. value,
  106. object[key],
  107. (namespace ? `${namespace}.` : "") + key.toString(),
  108. merger
  109. );
  110. } else {
  111. object[key] = value;
  112. }
  113. }
  114. return object;
  115. }
  116. function createDefu(merger) {
  117. return (...arguments_) => (
  118. // eslint-disable-next-line unicorn/no-array-reduce
  119. arguments_.reduce((p, c) => _defu(p, c, "", merger), {})
  120. );
  121. }
  122. const defu = createDefu();
  123. function isPlainObject(obj) {
  124. return Object.prototype.toString.call(obj) === "[object Object]";
  125. }
  126. function isLogObj(arg) {
  127. if (!isPlainObject(arg)) {
  128. return false;
  129. }
  130. if (!arg.message && !arg.args) {
  131. return false;
  132. }
  133. if (arg.stack) {
  134. return false;
  135. }
  136. return true;
  137. }
  138. let paused = false;
  139. const queue = [];
  140. class Consola {
  141. options;
  142. _lastLog;
  143. _mockFn;
  144. /**
  145. * Creates an instance of Consola with specified options or defaults.
  146. *
  147. * @param {Partial<ConsolaOptions>} [options={}] - Configuration options for the Consola instance.
  148. */
  149. constructor(options = {}) {
  150. const types = options.types || LogTypes;
  151. this.options = defu(
  152. {
  153. ...options,
  154. defaults: { ...options.defaults },
  155. level: _normalizeLogLevel(options.level, types),
  156. reporters: [...options.reporters || []]
  157. },
  158. {
  159. types: LogTypes,
  160. throttle: 1e3,
  161. throttleMin: 5,
  162. formatOptions: {
  163. date: true,
  164. colors: false,
  165. compact: true
  166. }
  167. }
  168. );
  169. for (const type in types) {
  170. const defaults = {
  171. type,
  172. ...this.options.defaults,
  173. ...types[type]
  174. };
  175. this[type] = this._wrapLogFn(defaults);
  176. this[type].raw = this._wrapLogFn(
  177. defaults,
  178. true
  179. );
  180. }
  181. if (this.options.mockFn) {
  182. this.mockTypes();
  183. }
  184. this._lastLog = {};
  185. }
  186. /**
  187. * Gets the current log level of the Consola instance.
  188. *
  189. * @returns {number} The current log level.
  190. */
  191. get level() {
  192. return this.options.level;
  193. }
  194. /**
  195. * Sets the minimum log level that will be output by the instance.
  196. *
  197. * @param {number} level - The new log level to set.
  198. */
  199. set level(level) {
  200. this.options.level = _normalizeLogLevel(
  201. level,
  202. this.options.types,
  203. this.options.level
  204. );
  205. }
  206. /**
  207. * Displays a prompt to the user and returns the response.
  208. * Throw an error if `prompt` is not supported by the current configuration.
  209. *
  210. * @template T
  211. * @param {string} message - The message to display in the prompt.
  212. * @param {T} [opts] - Optional options for the prompt. See {@link PromptOptions}.
  213. * @returns {promise<T>} A promise that infer with the prompt options. See {@link PromptOptions}.
  214. */
  215. prompt(message, opts) {
  216. if (!this.options.prompt) {
  217. throw new Error("prompt is not supported!");
  218. }
  219. return this.options.prompt(message, opts);
  220. }
  221. /**
  222. * Creates a new instance of Consola, inheriting options from the current instance, with possible overrides.
  223. *
  224. * @param {Partial<ConsolaOptions>} options - Optional overrides for the new instance. See {@link ConsolaOptions}.
  225. * @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
  226. */
  227. create(options) {
  228. const instance = new Consola({
  229. ...this.options,
  230. ...options
  231. });
  232. if (this._mockFn) {
  233. instance.mockTypes(this._mockFn);
  234. }
  235. return instance;
  236. }
  237. /**
  238. * Creates a new Consola instance with the specified default log object properties.
  239. *
  240. * @param {InputLogObject} defaults - Default properties to include in any log from the new instance. See {@link InputLogObject}.
  241. * @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
  242. */
  243. withDefaults(defaults) {
  244. return this.create({
  245. ...this.options,
  246. defaults: {
  247. ...this.options.defaults,
  248. ...defaults
  249. }
  250. });
  251. }
  252. /**
  253. * Creates a new Consola instance with a specified tag, which will be included in every log.
  254. *
  255. * @param {string} tag - The tag to include in each log of the new instance.
  256. * @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
  257. */
  258. withTag(tag) {
  259. return this.withDefaults({
  260. tag: this.options.defaults.tag ? this.options.defaults.tag + ":" + tag : tag
  261. });
  262. }
  263. /**
  264. * Adds a custom reporter to the Consola instance.
  265. * Reporters will be called for each log message, depending on their implementation and log level.
  266. *
  267. * @param {ConsolaReporter} reporter - The reporter to add. See {@link ConsolaReporter}.
  268. * @returns {Consola} The current Consola instance.
  269. */
  270. addReporter(reporter) {
  271. this.options.reporters.push(reporter);
  272. return this;
  273. }
  274. /**
  275. * Removes a custom reporter from the Consola instance.
  276. * If no reporter is specified, all reporters will be removed.
  277. *
  278. * @param {ConsolaReporter} reporter - The reporter to remove. See {@link ConsolaReporter}.
  279. * @returns {Consola} The current Consola instance.
  280. */
  281. removeReporter(reporter) {
  282. if (reporter) {
  283. const i = this.options.reporters.indexOf(reporter);
  284. if (i !== -1) {
  285. return this.options.reporters.splice(i, 1);
  286. }
  287. } else {
  288. this.options.reporters.splice(0);
  289. }
  290. return this;
  291. }
  292. /**
  293. * Replaces all reporters of the Consola instance with the specified array of reporters.
  294. *
  295. * @param {ConsolaReporter[]} reporters - The new reporters to set. See {@link ConsolaReporter}.
  296. * @returns {Consola} The current Consola instance.
  297. */
  298. setReporters(reporters) {
  299. this.options.reporters = Array.isArray(reporters) ? reporters : [reporters];
  300. return this;
  301. }
  302. wrapAll() {
  303. this.wrapConsole();
  304. this.wrapStd();
  305. }
  306. restoreAll() {
  307. this.restoreConsole();
  308. this.restoreStd();
  309. }
  310. /**
  311. * Overrides console methods with Consola logging methods for consistent logging.
  312. */
  313. wrapConsole() {
  314. for (const type in this.options.types) {
  315. if (!console["__" + type]) {
  316. console["__" + type] = console[type];
  317. }
  318. console[type] = this[type].raw;
  319. }
  320. }
  321. /**
  322. * Restores the original console methods, removing Consola overrides.
  323. */
  324. restoreConsole() {
  325. for (const type in this.options.types) {
  326. if (console["__" + type]) {
  327. console[type] = console["__" + type];
  328. delete console["__" + type];
  329. }
  330. }
  331. }
  332. /**
  333. * Overrides standard output and error streams to redirect them through Consola.
  334. */
  335. wrapStd() {
  336. this._wrapStream(this.options.stdout, "log");
  337. this._wrapStream(this.options.stderr, "log");
  338. }
  339. _wrapStream(stream, type) {
  340. if (!stream) {
  341. return;
  342. }
  343. if (!stream.__write) {
  344. stream.__write = stream.write;
  345. }
  346. stream.write = (data) => {
  347. this[type].raw(String(data).trim());
  348. };
  349. }
  350. /**
  351. * Restores the original standard output and error streams, removing the Consola redirection.
  352. */
  353. restoreStd() {
  354. this._restoreStream(this.options.stdout);
  355. this._restoreStream(this.options.stderr);
  356. }
  357. _restoreStream(stream) {
  358. if (!stream) {
  359. return;
  360. }
  361. if (stream.__write) {
  362. stream.write = stream.__write;
  363. delete stream.__write;
  364. }
  365. }
  366. /**
  367. * Pauses logging, queues incoming logs until resumed.
  368. */
  369. pauseLogs() {
  370. paused = true;
  371. }
  372. /**
  373. * Resumes logging, processing any queued logs.
  374. */
  375. resumeLogs() {
  376. paused = false;
  377. const _queue = queue.splice(0);
  378. for (const item of _queue) {
  379. item[0]._logFn(item[1], item[2]);
  380. }
  381. }
  382. /**
  383. * Replaces logging methods with mocks if a mock function is provided.
  384. *
  385. * @param {ConsolaOptions["mockFn"]} mockFn - The function to use for mocking logging methods. See {@link ConsolaOptions["mockFn"]}.
  386. */
  387. mockTypes(mockFn) {
  388. const _mockFn = mockFn || this.options.mockFn;
  389. this._mockFn = _mockFn;
  390. if (typeof _mockFn !== "function") {
  391. return;
  392. }
  393. for (const type in this.options.types) {
  394. this[type] = _mockFn(type, this.options.types[type]) || this[type];
  395. this[type].raw = this[type];
  396. }
  397. }
  398. _wrapLogFn(defaults, isRaw) {
  399. return (...args) => {
  400. if (paused) {
  401. queue.push([this, defaults, args, isRaw]);
  402. return;
  403. }
  404. return this._logFn(defaults, args, isRaw);
  405. };
  406. }
  407. _logFn(defaults, args, isRaw) {
  408. if ((defaults.level || 0) > this.level) {
  409. return false;
  410. }
  411. const logObj = {
  412. date: /* @__PURE__ */ new Date(),
  413. args: [],
  414. ...defaults,
  415. level: _normalizeLogLevel(defaults.level, this.options.types)
  416. };
  417. if (!isRaw && args.length === 1 && isLogObj(args[0])) {
  418. Object.assign(logObj, args[0]);
  419. } else {
  420. logObj.args = [...args];
  421. }
  422. if (logObj.message) {
  423. logObj.args.unshift(logObj.message);
  424. delete logObj.message;
  425. }
  426. if (logObj.additional) {
  427. if (!Array.isArray(logObj.additional)) {
  428. logObj.additional = logObj.additional.split("\n");
  429. }
  430. logObj.args.push("\n" + logObj.additional.join("\n"));
  431. delete logObj.additional;
  432. }
  433. logObj.type = typeof logObj.type === "string" ? logObj.type.toLowerCase() : "log";
  434. logObj.tag = typeof logObj.tag === "string" ? logObj.tag : "";
  435. const resolveLog = (newLog = false) => {
  436. const repeated = (this._lastLog.count || 0) - this.options.throttleMin;
  437. if (this._lastLog.object && repeated > 0) {
  438. const args2 = [...this._lastLog.object.args];
  439. if (repeated > 1) {
  440. args2.push(`(repeated ${repeated} times)`);
  441. }
  442. this._log({ ...this._lastLog.object, args: args2 });
  443. this._lastLog.count = 1;
  444. }
  445. if (newLog) {
  446. this._lastLog.object = logObj;
  447. this._log(logObj);
  448. }
  449. };
  450. clearTimeout(this._lastLog.timeout);
  451. const diffTime = this._lastLog.time && logObj.date ? logObj.date.getTime() - this._lastLog.time.getTime() : 0;
  452. this._lastLog.time = logObj.date;
  453. if (diffTime < this.options.throttle) {
  454. try {
  455. const serializedLog = JSON.stringify([
  456. logObj.type,
  457. logObj.tag,
  458. logObj.args
  459. ]);
  460. const isSameLog = this._lastLog.serialized === serializedLog;
  461. this._lastLog.serialized = serializedLog;
  462. if (isSameLog) {
  463. this._lastLog.count = (this._lastLog.count || 0) + 1;
  464. if (this._lastLog.count > this.options.throttleMin) {
  465. this._lastLog.timeout = setTimeout(
  466. resolveLog,
  467. this.options.throttle
  468. );
  469. return;
  470. }
  471. }
  472. } catch {
  473. }
  474. }
  475. resolveLog(true);
  476. }
  477. _log(logObj) {
  478. for (const reporter of this.options.reporters) {
  479. reporter.log(logObj, {
  480. options: this.options
  481. });
  482. }
  483. }
  484. }
  485. function _normalizeLogLevel(input, types = {}, defaultLevel = 3) {
  486. if (input === void 0) {
  487. return defaultLevel;
  488. }
  489. if (typeof input === "number") {
  490. return input;
  491. }
  492. if (types[input] && types[input].level !== void 0) {
  493. return types[input].level;
  494. }
  495. return defaultLevel;
  496. }
  497. Consola.prototype.add = Consola.prototype.addReporter;
  498. Consola.prototype.remove = Consola.prototype.removeReporter;
  499. Consola.prototype.clear = Consola.prototype.removeReporter;
  500. Consola.prototype.withScope = Consola.prototype.withTag;
  501. Consola.prototype.mock = Consola.prototype.mockTypes;
  502. Consola.prototype.pause = Consola.prototype.pauseLogs;
  503. Consola.prototype.resume = Consola.prototype.resumeLogs;
  504. function createConsola(options = {}) {
  505. return new Consola(options);
  506. }
  507. export { Consola, LogLevels, LogTypes, createConsola };