UNPKG

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