UNPKG

@terrazzo/parser

Version:

Parser/validator for the Design Tokens Community Group (DTCG) standard.

172 lines 5.95 kB
import * as momoa from '@humanwhocodes/momoa'; import { CachedWildcardMatcher } from '@terrazzo/token-tools'; import pc from 'picocolors'; import { codeFrameColumns } from './lib/code-frame.js'; export const LOG_ORDER = ['error', 'warn', 'info', 'debug']; const GROUP_COLOR = { config: pc.cyan, import: pc.green, lint: pc.yellowBright, parser: pc.magenta, plugin: pc.greenBright, resolver: pc.magentaBright, server: pc.gray, }; const MESSAGE_COLOR = { error: pc.red, warn: pc.yellow, info: (msg) => msg, debug: pc.gray, }; const timeFormatter = new Intl.DateTimeFormat('en-us', { hour: 'numeric', hour12: false, minute: 'numeric', second: 'numeric', fractionalSecondDigits: 3, }); /** * @param {Entry} entry * @param {Severity} severity * @return {string} */ export function formatMessage(entry, severity) { const groupColor = GROUP_COLOR[entry.group]; const messageColor = MESSAGE_COLOR[severity]; let message = entry.message; message = `${groupColor(`${entry.group}${entry.label ? `:${entry.label}` : ''}:`)} ${messageColor(message)}`; if (typeof entry.timing === 'number') { message = `${message} ${formatTiming(entry.timing)}`; } if (entry.node) { const start = entry.node?.loc?.start ?? { line: 0, column: 0 }; // strip "file://" protocol, but not href const loc = entry.filename ? `${entry.filename?.href.replace(/^file:\/\//, '')}:${start?.line ?? 0}:${start?.column ?? 0}\n\n` : ''; const codeFrame = codeFrameColumns(entry.src ?? momoa.print(entry.node, { indent: 2 }), { start }, { highlightCode: false }); message = `${message}\n\n${loc}${codeFrame}`; } return message; } const debugMatch = new CachedWildcardMatcher(); export default class Logger { level = 'info'; debugScope = '*'; errorCount = 0; warnCount = 0; infoCount = 0; debugCount = 0; constructor(options) { if (options?.level) { this.level = options.level; } if (options?.debugScope) { this.debugScope = options.debugScope; } } setLevel(level) { this.level = level; } /** Log an error message (always; can’t be silenced) */ error(...entries) { const message = []; let firstNode; for (const entry of entries) { this.errorCount++; message.push(formatMessage(entry, 'error')); if (entry.node) { firstNode = entry.node; } } if (entries.every((e) => e.continueOnError)) { // oxlint-disable-next-line no-console -- this is a logger console.error(message.join('\n\n')); } else { const e = firstNode ? new TokensJSONError(message.join('\n\n')) : new Error(message.join('\n\n')); throw e; } } /** Log an info message (if logging level permits) */ info(...entries) { for (const entry of entries) { this.infoCount++; if (this.level === 'silent' || LOG_ORDER.indexOf(this.level) < LOG_ORDER.indexOf('info')) { return; } const message = formatMessage(entry, 'info'); // oxlint-disable-next-line no-console -- this is a logger console.log(message); } } /** Log a warning message (if logging level permits) */ warn(...entries) { for (const entry of entries) { this.warnCount++; if (this.level === 'silent' || LOG_ORDER.indexOf(this.level) < LOG_ORDER.indexOf('warn')) { return; } const message = formatMessage(entry, 'warn'); // oxlint-disable-next-line no-console -- this is a logger console.warn(message); } } /** Log a diagnostics message (if logging level permits) */ debug(...entries) { for (const entry of entries) { if (this.level === 'silent' || LOG_ORDER.indexOf(this.level) < LOG_ORDER.indexOf('debug')) { return; } this.debugCount++; let message = formatMessage(entry, 'debug'); const debugPrefix = entry.label ? `${entry.group}:${entry.label}` : entry.group; if (this.debugScope !== '*' && !debugMatch.match(this.debugScope)(debugPrefix)) { return; } // debug color message .replace(/\[config[^\]]+\]/, (match) => pc.green(match)) .replace(/\[parser[^\]]+\]/, (match) => pc.magenta(match)) .replace(/\[lint[^\]]+\]/, (match) => pc.yellow(match)) .replace(/\[plugin[^\]]+\]/, (match) => pc.cyan(match)); message = `${pc.dim(timeFormatter.format(performance.now()))} ${message}`; if (typeof entry.timing === 'number') { message = `${message} ${formatTiming(entry.timing)}`; } // oxlint-disable-next-line no-console -- this is a logger console.log(message); } } /** Get stats for current logger instance */ stats() { return { errorCount: this.errorCount, warnCount: this.warnCount, infoCount: this.infoCount, debugCount: this.debugCount, }; } } function formatTiming(timing) { let output = ''; if (timing < 1_000) { output = `${Math.round(timing * 100) / 100}ms`; } else if (timing < 60_000) { output = `${Math.round(timing) / 1_000}s`; } else { output = `${Math.round(timing / 1_000) / 60}m`; } return pc.dim(`[${output}]`); } export class TokensJSONError extends Error { constructor(message) { super(message); this.name = 'TokensJSONError'; } } //# sourceMappingURL=logger.js.map