'use strict' const log = require('proc-log') const { format } = require('util') // helper to emit log messages with a predefined prefix const withPrefix = (prefix) => log.LEVELS.reduce((acc, level) => { acc[level] = (...args) => log[level](prefix, ...args) return acc }, {}) // very basic ansi color generator const COLORS = { wrap: (str, colors) => { const codes = colors.filter(c => typeof c === 'number') return `\x1b[${codes.join(';')}m${str}\x1b[0m` }, inverse: 7, fg: { black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37 }, bg: { black: 40, red: 41, green: 42, yellow: 43, blue: 44, magenta: 45, cyan: 46, white: 47 } } class Logger { #buffer = [] #paused = null #level = null #stream = null // ordered from loudest to quietest #levels = [{ id: 'silly', display: 'sill', style: { inverse: true } }, { id: 'verbose', display: 'verb', style: { fg: 'cyan', bg: 'black' } }, { id: 'info', style: { fg: 'green' } }, { id: 'http', style: { fg: 'green', bg: 'black' } }, { id: 'notice', style: { fg: 'cyan', bg: 'black' } }, { id: 'warn', display: 'WARN', style: { fg: 'black', bg: 'yellow' } }, { id: 'error', display: 'ERR!', style: { fg: 'red', bg: 'black' } }] constructor (stream) { process.on('log', (...args) => this._onLog(...args)) this.#levels = new Map(this.#levels.map((level, index) => [level.id, { ...level, index }])) this.level = 'info' this.stream = stream log.pause() } get stream () { return this.#stream } set stream (stream) { this.#stream = stream } get level () { return this.#levels.get(this.#level) || null } set level (level) { this.#level = (this.#levels.get(level) && this.#levels.get(level).id) || null } isVisible (level) { return (this.level && this.level.index <= (this.#levels.get(level) && this.#levels.get(level).index) || -1) } _onLog (...args) { const [level] = args if (level === 'pause') { this.#paused = true return } if (level === 'resume') { this.#paused = false this.#buffer.forEach((b) => this._log(...b)) this.#buffer.length = 0 return } if (this.#paused) { this.#buffer.push(args) return } this._log(...args) } _color (str, { fg, bg, inverse }) { if (!this.#stream || !this.#stream.isTTY) { return str } return COLORS.wrap(str, [ COLORS.fg[fg], COLORS.bg[bg], inverse && COLORS.inverse ]) } _log (levelId, msgPrefix, ...args) { if (!this.isVisible(levelId) || (!this.#stream || typeof this.#stream.write !== 'function')) { return } const level = this.#levels.get(levelId) const prefixParts = [ this._color('gyp', { fg: 'white', bg: 'black' }), this._color(level.display || level.id, level.style) ] if (msgPrefix) { prefixParts.push(this._color(msgPrefix, { fg: 'magenta' })) } const prefix = prefixParts.join(' ').trim() + ' ' const lines = format(...args).split(/\r?\n/).map(l => prefix + l.trim()) this.#stream.write(lines.join('\n') + '\n') } } // used to suppress logs in tests const NULL_LOGGER = !!process.env.NODE_GYP_NULL_LOGGER module.exports = { logger: new Logger(NULL_LOGGER ? null : process.stderr), stdout: NULL_LOGGER ? () => {} : (...args) => console.log(...args), withPrefix, ...log }