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