createConsoleLogger.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { LogType } = require("./Logger");
  7. /** @typedef {import("../../declarations/WebpackOptions").FilterItemTypes} FilterItemTypes */
  8. /** @typedef {import("../../declarations/WebpackOptions").FilterTypes} FilterTypes */
  9. /** @typedef {import("./Logger").LogTypeEnum} LogTypeEnum */
  10. /** @typedef {(item: string) => boolean} FilterFunction */
  11. /** @typedef {(value: string, type: LogTypeEnum, args?: EXPECTED_ANY[]) => void} LoggingFunction */
  12. /**
  13. * @typedef {object} LoggerConsole
  14. * @property {() => void} clear
  15. * @property {() => void} trace
  16. * @property {(...args: EXPECTED_ANY[]) => void} info
  17. * @property {(...args: EXPECTED_ANY[]) => void} log
  18. * @property {(...args: EXPECTED_ANY[]) => void} warn
  19. * @property {(...args: EXPECTED_ANY[]) => void} error
  20. * @property {(...args: EXPECTED_ANY[]) => void=} debug
  21. * @property {(...args: EXPECTED_ANY[]) => void=} group
  22. * @property {(...args: EXPECTED_ANY[]) => void=} groupCollapsed
  23. * @property {(...args: EXPECTED_ANY[]) => void=} groupEnd
  24. * @property {(...args: EXPECTED_ANY[]) => void=} status
  25. * @property {(...args: EXPECTED_ANY[]) => void=} profile
  26. * @property {(...args: EXPECTED_ANY[]) => void=} profileEnd
  27. * @property {(...args: EXPECTED_ANY[]) => void=} logTime
  28. */
  29. /**
  30. * @typedef {object} LoggerOptions
  31. * @property {false|true|"none"|"error"|"warn"|"info"|"log"|"verbose"} level loglevel
  32. * @property {FilterTypes|boolean} debug filter for debug logging
  33. * @property {LoggerConsole} console the console to log to
  34. */
  35. /**
  36. * @param {FilterItemTypes} item an input item
  37. * @returns {FilterFunction | undefined} filter function
  38. */
  39. const filterToFunction = item => {
  40. if (typeof item === "string") {
  41. const regExp = new RegExp(
  42. `[\\\\/]${item.replace(/[-[\]{}()*+?.\\^$|]/g, "\\$&")}([\\\\/]|$|!|\\?)`
  43. );
  44. return ident => regExp.test(ident);
  45. }
  46. if (item && typeof item === "object" && typeof item.test === "function") {
  47. return ident => item.test(ident);
  48. }
  49. if (typeof item === "function") {
  50. return item;
  51. }
  52. if (typeof item === "boolean") {
  53. return () => item;
  54. }
  55. };
  56. /**
  57. * @enum {number}
  58. */
  59. const LogLevel = {
  60. none: 6,
  61. false: 6,
  62. error: 5,
  63. warn: 4,
  64. info: 3,
  65. log: 2,
  66. true: 2,
  67. verbose: 1
  68. };
  69. /**
  70. * @param {LoggerOptions} options options object
  71. * @returns {LoggingFunction} logging function
  72. */
  73. module.exports = ({ level = "info", debug = false, console }) => {
  74. const debugFilters =
  75. /** @type {FilterFunction[]} */
  76. (
  77. typeof debug === "boolean"
  78. ? [() => debug]
  79. : /** @type {FilterItemTypes[]} */ ([])
  80. .concat(debug)
  81. .map(filterToFunction)
  82. );
  83. const loglevel = LogLevel[`${level}`] || 0;
  84. /**
  85. * @param {string} name name of the logger
  86. * @param {LogTypeEnum} type type of the log entry
  87. * @param {EXPECTED_ANY[]=} args arguments of the log entry
  88. * @returns {void}
  89. */
  90. const logger = (name, type, args) => {
  91. const labeledArgs = () => {
  92. if (Array.isArray(args)) {
  93. if (args.length > 0 && typeof args[0] === "string") {
  94. return [`[${name}] ${args[0]}`, ...args.slice(1)];
  95. }
  96. return [`[${name}]`, ...args];
  97. }
  98. return [];
  99. };
  100. const debug = debugFilters.some(f => f(name));
  101. switch (type) {
  102. case LogType.debug:
  103. if (!debug) return;
  104. if (typeof console.debug === "function") {
  105. console.debug(...labeledArgs());
  106. } else {
  107. console.log(...labeledArgs());
  108. }
  109. break;
  110. case LogType.log:
  111. if (!debug && loglevel > LogLevel.log) return;
  112. console.log(...labeledArgs());
  113. break;
  114. case LogType.info:
  115. if (!debug && loglevel > LogLevel.info) return;
  116. console.info(...labeledArgs());
  117. break;
  118. case LogType.warn:
  119. if (!debug && loglevel > LogLevel.warn) return;
  120. console.warn(...labeledArgs());
  121. break;
  122. case LogType.error:
  123. if (!debug && loglevel > LogLevel.error) return;
  124. console.error(...labeledArgs());
  125. break;
  126. case LogType.trace:
  127. if (!debug) return;
  128. console.trace();
  129. break;
  130. case LogType.groupCollapsed:
  131. if (!debug && loglevel > LogLevel.log) return;
  132. if (!debug && loglevel > LogLevel.verbose) {
  133. if (typeof console.groupCollapsed === "function") {
  134. console.groupCollapsed(...labeledArgs());
  135. } else {
  136. console.log(...labeledArgs());
  137. }
  138. break;
  139. }
  140. // falls through
  141. case LogType.group:
  142. if (!debug && loglevel > LogLevel.log) return;
  143. if (typeof console.group === "function") {
  144. console.group(...labeledArgs());
  145. } else {
  146. console.log(...labeledArgs());
  147. }
  148. break;
  149. case LogType.groupEnd:
  150. if (!debug && loglevel > LogLevel.log) return;
  151. if (typeof console.groupEnd === "function") {
  152. console.groupEnd();
  153. }
  154. break;
  155. case LogType.time: {
  156. if (!debug && loglevel > LogLevel.log) return;
  157. const [label, start, end] =
  158. /** @type {[string, number, number]} */
  159. (args);
  160. const ms = start * 1000 + end / 1000000;
  161. const msg = `[${name}] ${label}: ${ms} ms`;
  162. if (typeof console.logTime === "function") {
  163. console.logTime(msg);
  164. } else {
  165. console.log(msg);
  166. }
  167. break;
  168. }
  169. case LogType.profile:
  170. if (typeof console.profile === "function") {
  171. console.profile(...labeledArgs());
  172. }
  173. break;
  174. case LogType.profileEnd:
  175. if (typeof console.profileEnd === "function") {
  176. console.profileEnd(...labeledArgs());
  177. }
  178. break;
  179. case LogType.clear:
  180. if (!debug && loglevel > LogLevel.log) return;
  181. if (typeof console.clear === "function") {
  182. console.clear();
  183. }
  184. break;
  185. case LogType.status:
  186. if (!debug && loglevel > LogLevel.info) return;
  187. if (typeof console.status === "function") {
  188. if (!args || args.length === 0) {
  189. console.status();
  190. } else {
  191. console.status(...labeledArgs());
  192. }
  193. } else if (args && args.length !== 0) {
  194. console.info(...labeledArgs());
  195. }
  196. break;
  197. default:
  198. throw new Error(`Unexpected LogType ${type}`);
  199. }
  200. };
  201. return logger;
  202. };