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.

210 lines
6.1 KiB

4 years ago
  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. */
  45. const LogLevel = {
  46. none: 6,
  47. false: 6,
  48. error: 5,
  49. warn: 4,
  50. info: 3,
  51. log: 2,
  52. true: 2,
  53. verbose: 1
  54. };
  55. /**
  56. * @param {LoggerOptions} options options object
  57. * @returns {function(string, LogTypeEnum, any[]): void} logging function
  58. */
  59. module.exports = ({ level = "info", debug = false, console }) => {
  60. const debugFilters =
  61. typeof debug === "boolean"
  62. ? [() => debug]
  63. : /** @type {FilterItemTypes[]} */ ([])
  64. .concat(debug)
  65. .map(filterToFunction);
  66. /** @type {number} */
  67. const loglevel = LogLevel[`${level}`] || 0;
  68. /**
  69. * @param {string} name name of the logger
  70. * @param {LogTypeEnum} type type of the log entry
  71. * @param {any[]} args arguments of the log entry
  72. * @returns {void}
  73. */
  74. const logger = (name, type, args) => {
  75. const labeledArgs = () => {
  76. if (Array.isArray(args)) {
  77. if (args.length > 0 && typeof args[0] === "string") {
  78. return [`[${name}] ${args[0]}`, ...args.slice(1)];
  79. } else {
  80. return [`[${name}]`, ...args];
  81. }
  82. } else {
  83. return [];
  84. }
  85. };
  86. const debug = debugFilters.some(f => f(name));
  87. switch (type) {
  88. case LogType.debug:
  89. if (!debug) return;
  90. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  91. if (typeof console.debug === "function") {
  92. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  93. console.debug(...labeledArgs());
  94. } else {
  95. console.log(...labeledArgs());
  96. }
  97. break;
  98. case LogType.log:
  99. if (!debug && loglevel > LogLevel.log) return;
  100. console.log(...labeledArgs());
  101. break;
  102. case LogType.info:
  103. if (!debug && loglevel > LogLevel.info) return;
  104. console.info(...labeledArgs());
  105. break;
  106. case LogType.warn:
  107. if (!debug && loglevel > LogLevel.warn) return;
  108. console.warn(...labeledArgs());
  109. break;
  110. case LogType.error:
  111. if (!debug && loglevel > LogLevel.error) return;
  112. console.error(...labeledArgs());
  113. break;
  114. case LogType.trace:
  115. if (!debug) return;
  116. console.trace();
  117. break;
  118. case LogType.groupCollapsed:
  119. if (!debug && loglevel > LogLevel.log) return;
  120. if (!debug && loglevel > LogLevel.verbose) {
  121. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  122. if (typeof console.groupCollapsed === "function") {
  123. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  124. console.groupCollapsed(...labeledArgs());
  125. } else {
  126. console.log(...labeledArgs());
  127. }
  128. break;
  129. }
  130. // falls through
  131. case LogType.group:
  132. if (!debug && loglevel > LogLevel.log) return;
  133. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  134. if (typeof console.group === "function") {
  135. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  136. console.group(...labeledArgs());
  137. } else {
  138. console.log(...labeledArgs());
  139. }
  140. break;
  141. case LogType.groupEnd:
  142. if (!debug && loglevel > LogLevel.log) return;
  143. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  144. if (typeof console.groupEnd === "function") {
  145. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  146. console.groupEnd();
  147. }
  148. break;
  149. case LogType.time: {
  150. if (!debug && loglevel > LogLevel.log) return;
  151. const ms = args[1] * 1000 + args[2] / 1000000;
  152. const msg = `[${name}] ${args[0]}: ${ms}ms`;
  153. if (typeof console.logTime === "function") {
  154. console.logTime(msg);
  155. } else {
  156. console.log(msg);
  157. }
  158. break;
  159. }
  160. case LogType.profile:
  161. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  162. if (typeof console.profile === "function") {
  163. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  164. console.profile(...labeledArgs());
  165. }
  166. break;
  167. case LogType.profileEnd:
  168. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  169. if (typeof console.profileEnd === "function") {
  170. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  171. console.profileEnd(...labeledArgs());
  172. }
  173. break;
  174. case LogType.clear:
  175. if (!debug && loglevel > LogLevel.log) return;
  176. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  177. if (typeof console.clear === "function") {
  178. // eslint-disable-next-line node/no-unsupported-features/node-builtins
  179. console.clear();
  180. }
  181. break;
  182. case LogType.status:
  183. if (!debug && loglevel > LogLevel.info) return;
  184. if (typeof console.status === "function") {
  185. if (args.length === 0) {
  186. console.status();
  187. } else {
  188. console.status(...labeledArgs());
  189. }
  190. } else {
  191. if (args.length !== 0) {
  192. console.info(...labeledArgs());
  193. }
  194. }
  195. break;
  196. default:
  197. throw new Error(`Unexpected LogType ${type}`);
  198. }
  199. };
  200. return logger;
  201. };