createConsoleLogger.js 6.1 KB

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