|
| 1 | +/* eslint-disable no-console */ |
| 2 | +/* eslint-disable @typescript-eslint/restrict-plus-operands */ |
| 3 | +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ |
| 4 | +/* eslint-env browser */ |
| 5 | + |
| 6 | +/** |
| 7 | + * This is the web browser implementation of `debug()`. |
| 8 | + */ |
| 9 | +import humanize from 'ms' |
| 10 | +import setup from './common.js' |
| 11 | + |
| 12 | +const storage = localstorage() |
| 13 | + |
| 14 | +/** |
| 15 | + * Colors. |
| 16 | + */ |
| 17 | +const colors = [ |
| 18 | + '#0000CC', |
| 19 | + '#0000FF', |
| 20 | + '#0033CC', |
| 21 | + '#0033FF', |
| 22 | + '#0066CC', |
| 23 | + '#0066FF', |
| 24 | + '#0099CC', |
| 25 | + '#0099FF', |
| 26 | + '#00CC00', |
| 27 | + '#00CC33', |
| 28 | + '#00CC66', |
| 29 | + '#00CC99', |
| 30 | + '#00CCCC', |
| 31 | + '#00CCFF', |
| 32 | + '#3300CC', |
| 33 | + '#3300FF', |
| 34 | + '#3333CC', |
| 35 | + '#3333FF', |
| 36 | + '#3366CC', |
| 37 | + '#3366FF', |
| 38 | + '#3399CC', |
| 39 | + '#3399FF', |
| 40 | + '#33CC00', |
| 41 | + '#33CC33', |
| 42 | + '#33CC66', |
| 43 | + '#33CC99', |
| 44 | + '#33CCCC', |
| 45 | + '#33CCFF', |
| 46 | + '#6600CC', |
| 47 | + '#6600FF', |
| 48 | + '#6633CC', |
| 49 | + '#6633FF', |
| 50 | + '#66CC00', |
| 51 | + '#66CC33', |
| 52 | + '#9900CC', |
| 53 | + '#9900FF', |
| 54 | + '#9933CC', |
| 55 | + '#9933FF', |
| 56 | + '#99CC00', |
| 57 | + '#99CC33', |
| 58 | + '#CC0000', |
| 59 | + '#CC0033', |
| 60 | + '#CC0066', |
| 61 | + '#CC0099', |
| 62 | + '#CC00CC', |
| 63 | + '#CC00FF', |
| 64 | + '#CC3300', |
| 65 | + '#CC3333', |
| 66 | + '#CC3366', |
| 67 | + '#CC3399', |
| 68 | + '#CC33CC', |
| 69 | + '#CC33FF', |
| 70 | + '#CC6600', |
| 71 | + '#CC6633', |
| 72 | + '#CC9900', |
| 73 | + '#CC9933', |
| 74 | + '#CCCC00', |
| 75 | + '#CCCC33', |
| 76 | + '#FF0000', |
| 77 | + '#FF0033', |
| 78 | + '#FF0066', |
| 79 | + '#FF0099', |
| 80 | + '#FF00CC', |
| 81 | + '#FF00FF', |
| 82 | + '#FF3300', |
| 83 | + '#FF3333', |
| 84 | + '#FF3366', |
| 85 | + '#FF3399', |
| 86 | + '#FF33CC', |
| 87 | + '#FF33FF', |
| 88 | + '#FF6600', |
| 89 | + '#FF6633', |
| 90 | + '#FF9900', |
| 91 | + '#FF9933', |
| 92 | + '#FFCC00', |
| 93 | + '#FFCC33' |
| 94 | +] |
| 95 | + |
| 96 | +/** |
| 97 | + * Currently only WebKit-based Web Inspectors, Firefox >= v31, |
| 98 | + * and the Firebug extension (any Firefox version) are known |
| 99 | + * to support "%c" CSS customizations. |
| 100 | + * |
| 101 | + * TODO: add a `localStorage` variable to explicitly enable/disable colors |
| 102 | + */ |
| 103 | + |
| 104 | +// eslint-disable-next-line complexity |
| 105 | +function useColors (): boolean { |
| 106 | + // NB: In an Electron preload script, document will be defined but not fully |
| 107 | + // initialized. Since we know we're in Chrome, we'll just detect this case |
| 108 | + // explicitly |
| 109 | + // @ts-expect-error window.process.type and window.process.__nwjs are not in the types |
| 110 | + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { |
| 111 | + return true |
| 112 | + } |
| 113 | + |
| 114 | + // Internet Explorer and Edge do not support colors. |
| 115 | + if (typeof navigator !== 'undefined' && navigator.userAgent && (navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/) != null)) { |
| 116 | + return false |
| 117 | + } |
| 118 | + |
| 119 | + // Is webkit? http://stackoverflow.com/a/16459606/376773 |
| 120 | + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 |
| 121 | + // @ts-expect-error document.documentElement.style.WebkitAppearance is not in the types |
| 122 | + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || |
| 123 | + // Is firebug? http://stackoverflow.com/a/398120/376773 |
| 124 | + // @ts-expect-error window.console.firebug and window.console.exception are not in the types |
| 125 | + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || |
| 126 | + // Is firefox >= v31? |
| 127 | + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages |
| 128 | + (typeof navigator !== 'undefined' && navigator.userAgent && (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) != null) && parseInt(RegExp.$1, 10) >= 31) || |
| 129 | + // Double check webkit in userAgent just in case we are in a worker |
| 130 | + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)) |
| 131 | +} |
| 132 | + |
| 133 | +/** |
| 134 | + * Colorize log arguments if enabled. |
| 135 | + */ |
| 136 | +function formatArgs (this: any, args: any[]): void { |
| 137 | + args[0] = (this.useColors ? '%c' : '') + |
| 138 | + this.namespace + |
| 139 | + (this.useColors ? ' %c' : ' ') + |
| 140 | + args[0] + |
| 141 | + (this.useColors ? '%c ' : ' ') + |
| 142 | + '+' + humanize(this.diff) |
| 143 | + |
| 144 | + if (!this.useColors) { |
| 145 | + return |
| 146 | + } |
| 147 | + |
| 148 | + const c = 'color: ' + this.color |
| 149 | + args.splice(1, 0, c, 'color: inherit') |
| 150 | + |
| 151 | + // The final "%c" is somewhat tricky, because there could be other |
| 152 | + // arguments passed either before or after the %c, so we need to |
| 153 | + // figure out the correct index to insert the CSS into |
| 154 | + let index = 0 |
| 155 | + let lastC = 0 |
| 156 | + args[0].replace(/%[a-zA-Z%]/g, (match: string) => { |
| 157 | + if (match === '%%') { |
| 158 | + return |
| 159 | + } |
| 160 | + index++ |
| 161 | + if (match === '%c') { |
| 162 | + // We only are interested in the *last* %c |
| 163 | + // (the user may have provided their own) |
| 164 | + lastC = index |
| 165 | + } |
| 166 | + }) |
| 167 | + |
| 168 | + args.splice(lastC, 0, c) |
| 169 | +} |
| 170 | + |
| 171 | +/** |
| 172 | + * Invokes `console.debug()` when available. |
| 173 | + * No-op when `console.debug` is not a "function". |
| 174 | + * If `console.debug` is not available, falls back |
| 175 | + * to `console.log`. |
| 176 | + */ |
| 177 | +const log = console.debug ?? console.log ?? (() => { }) |
| 178 | + |
| 179 | +/** |
| 180 | + * Save `namespaces`. |
| 181 | + * |
| 182 | + * @param {string} namespaces |
| 183 | + */ |
| 184 | +function save (namespaces: string): void { |
| 185 | + try { |
| 186 | + if (namespaces) { |
| 187 | + storage?.setItem('debug', namespaces) |
| 188 | + } else { |
| 189 | + storage?.removeItem('debug') |
| 190 | + } |
| 191 | + } catch (error) { |
| 192 | + // Swallow |
| 193 | + // XXX (@Qix-) should we be logging these? |
| 194 | + } |
| 195 | +} |
| 196 | + |
| 197 | +/** |
| 198 | + * Load `namespaces`. |
| 199 | + * |
| 200 | + * @returns {string} returns the previously persisted debug modes |
| 201 | + */ |
| 202 | +function load (): string | null | undefined { |
| 203 | + let r |
| 204 | + try { |
| 205 | + r = storage?.getItem('debug') |
| 206 | + } catch (error) { |
| 207 | + // Swallow |
| 208 | + // XXX (@Qix-) should we be logging these? |
| 209 | + } |
| 210 | + |
| 211 | + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG |
| 212 | + if (!r && typeof process !== 'undefined' && 'env' in process) { |
| 213 | + r = process.env.DEBUG |
| 214 | + } |
| 215 | + |
| 216 | + return r |
| 217 | +} |
| 218 | + |
| 219 | +/** |
| 220 | + * Localstorage attempts to return the localstorage. |
| 221 | + * |
| 222 | + * This is necessary because safari throws |
| 223 | + * when a user disables cookies/localstorage |
| 224 | + * and you attempt to access it. |
| 225 | + */ |
| 226 | +function localstorage (): Storage | undefined { |
| 227 | + try { |
| 228 | + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context |
| 229 | + // The Browser also has localStorage in the global context. |
| 230 | + return localStorage |
| 231 | + } catch (error) { |
| 232 | + // Swallow |
| 233 | + // XXX (@Qix-) should we be logging these? |
| 234 | + } |
| 235 | +} |
| 236 | + |
| 237 | +function setupFormatters (formatters: any): void { |
| 238 | + /** |
| 239 | + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. |
| 240 | + */ |
| 241 | + formatters.j = function (v: any) { |
| 242 | + try { |
| 243 | + return JSON.stringify(v) |
| 244 | + } catch (error: any) { |
| 245 | + return '[UnexpectedJSONParseError]: ' + error.message |
| 246 | + } |
| 247 | + } |
| 248 | +} |
| 249 | + |
| 250 | +export default setup({ formatArgs, save, load, useColors, setupFormatters, colors, storage, log }) |
0 commit comments