createConsoleLogger.js 6.2 KB

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