UNPKG

ololog

Version:

Platform-agnostic logging with colors in terminals & browsers / shows call locations / pretty prints objects and stacktraces / smart newline and indentation handling

167 lines (98 loc) 5.51 kB
"use strict"; /* ------------------------------------------------------------------------ */ const O = Object , StackTracey = require ('stacktracey') , ansi = require ('ansicolor') , bullet = require ('string.bullet') , pipez = require ('pipez') /* ------------------------------------------------------------------------ */ const stringify = require ('string.ify').configure ({ formatter (x) { if ((x instanceof Error) && !(typeof Symbol !== 'undefined' && x[Symbol.for ('String.ify')])) { const why = stringify.limit ((x.message || '').replace (/\r|\n/g, '').trim (), 120), stack = new StackTracey (x).pretty, stackIndented = stack.split ('\n').map (x => ' ' + x).join ('\n') return `[EXCEPTION] ${why}\n\n${stackIndented}\n` } } }) /* ------------------------------------------------------------------------ */ const { isBlank, blank } = require ('printable-characters') , changeLastNonemptyLine = (lines, fn) => { for (let i = lines.length - 1; i >= 0; i--) { if ((i === 0) || !isBlank (lines[i])) { lines[i] = fn (lines[i]) break; } } return lines } /* ------------------------------------------------------------------------ */ const log = pipez ({ /* ------------------------------------------------------------------------ */ stringify: (args, cfg, print = stringify.configure (cfg)) => args.map (arg => (typeof arg === 'string') ? arg : print (arg)), trim: (tokens, { max = undefined }) => !max ? tokens : tokens.map (t => stringify.limit (t, max)), lines: (tokens, { linebreak = '\n' }) => { let lines = [[]] let leftPad = [] for (const t of tokens) { const [first, ...rest] = t.split (linebreak) lines[lines.length - 1].push (first) lines = [...lines, ...rest.map (t => t ? [...leftPad, t] : [])] const pad = blank (!rest.length ? t : rest[rest.length - 1]) if (pad) { leftPad.push (pad) } } return lines }, concat: (lines, { separator = ' ' }) => lines.map (tokens => tokens.join (separator)), indent: (lines, { level = 0, pattern = '\t' }) => lines.map (line => pattern.repeat (level) + line), time: (lines, { when = new Date (), print = when => ansi.dim (when.toISOString ()) + '\t' }) => bullet (print (when), lines), locate: (lines, { shift = 0, where = (new StackTracey ().clean.at (2 + shift)), join = ((a, sep, b) => (a && b) ? (a + sep + b) : (a || b)), print = ({ calleeShort, fileName = [], line = [] }) => ansi.dim ('(' + join (calleeShort, ' @ ', join (fileName, ':', line)) + ')') }) => changeLastNonemptyLine (lines, line => join (line, ' ', print (where))), join: (lines, { linebreak = '\n' }) => lines.join (linebreak), render: (text, { engine = ((typeof window !== 'undefined') && (window.window === window) && window.navigator) ? (navigator.userAgent.indexOf ('Chrome') >= 0) ? 'chrome' : 'generic' : 'ansi', engines = { /* configurable */ }, consoleMethod = 'log', defaults = { ansi: s => console[consoleMethod] (s), chrome: s => console[consoleMethod] (...ansi.parse (s).asChromeConsoleLogArguments), generic: s => console[consoleMethod] (ansi.strip (s)) } }) => ((text && O.assign (defaults, engines)[engine] (text), text)), returnValue: (__, { initialArguments: [firstArgument] }) => firstArgument /* ------------------------------------------------------------------------ */ }).configure ({ time: false // disables 'time' step (until enabled back explicitly) /* ------------------------------------------------------------------------ */ }).methods ({ indent (level) { return this.configure ({ indent: { level: level }}) }, get error () { return this.configure ({ render: { consoleMethod: 'error' } }) }, get warn () { return this.configure ({ render: { consoleMethod: 'warn' } }) }, get info () { return this.configure ({ render: { consoleMethod: 'info' } }) }, maxArrayLength (n) { return this.configure ({ stringify: { maxArrayLength: n } }) }, maxDepth (n) { return this.configure ({ stringify: { maxDepth: n } }) }, get unlimited () { return this.configure ({ stringify: { maxArrayLength: Number.MAX_VALUE, maxDepth: Number.MAX_VALUE } }) }, get noPretty () { return this.configure ({ stringify: { pretty: false } }) }, get serialize () { return this.before ('render') }, get deserialize () { return this.from ('render') }, newline () { return this.from ('join')(['']) } }) /* ------------------------------------------------------------------------ */ ansi.names.forEach (color => { log.methods ({ get [color] () { return this.configure ({ 'concat+': lines => lines.map (ansi[color]) }) } }) }) /* ------------------------------------------------------------------------ */ module.exports = log /* ------------------------------------------------------------------------ */