UNPKG

wakitsu

Version:

Hobby project for managing anime watch list on Kitsu through CLI

210 lines 9.11 kB
import { createSpinner } from '../cli/cli-spinner.js'; import { pathBasename } from '../utils.js'; import { PrinterColor } from './print-colors.js'; import { inspect } from 'util'; import readline from 'readline'; const _leftLogMargin = 3; const _defaultIndent = 3; const _maxLogLength = 70; const _colorText = PrinterColor.colorText; export class Printer { static printInfo(message, header, marginOffset = 0) { const msgs = typeof message == 'string' ? [['p', `;g;${message}`, marginOffset]] : message.map((m) => ['p', `;g;${m}`, marginOffset]); this.print([null, ['h1', [`;bc;${header ?? 'INFO'}`], marginOffset], ...msgs, null]); } static printWarning(message, header, marginOffset = 0) { const msgs = typeof message == 'string' ? [['p', `;c;${message}`, marginOffset]] : message.map((m) => ['p', `;c;${m}`, marginOffset]); this.print([null, ['h1', [`;by;${header ?? 'WARNING'}`], marginOffset], ...msgs, null]); } static printError(message, header, marginOffset = 0) { const msgs = typeof message == 'string' ? [['p', `;y;${message}`, marginOffset]] : message.map((m) => ['p', `;y;${m}`, marginOffset]); this.print([null, ['h1', [`;r;${header ?? 'ERROR'}`], marginOffset], ...msgs, null]); } static print(logs) { logs.forEach((log) => { printLog(log); }); } static debug(...args) { const stack = Error('').stack; if (!stack) return; const [offender, filePath, lineNumber] = getStackInfo(stack); this.print([null, null, ['h1', [';br;DEBUG']], ['', `;br;${'─'.repeat(65)};x;\n`]]); [...args].forEach((a) => console.log(inspect(a, { showHidden: true, depth: null, colors: true }))); this.print([ null, ['', `;br;${'─'.repeat(65)};x;\n`], [ 'p', `;g;Exec ;x;by ;g;${offender || 'root'} ;x;in file ;g;${pathBasename(filePath)} ;x;at line ;m;${lineNumber}`, ], null, null, ]); console.log(''); } static printLoader(text, topPadding = 1) { Printer.print([...new Array(topPadding).fill(null, 0, topPadding)]); const spinner = createSpinner(_colorText(`${' '.repeat(3)};bg;@@@@@@ ;bb;${text} ;bg;@@@@@@`)); spinner.start(13); return () => { spinner.stop(); this.print([['h3', [text]]]); }; } static async prompt(query, promptFormat) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((rs) => { const querySentences = createFixedWidthSentences(query, -_leftLogMargin, 50); Printer.print([null, ['', `;g;┌${'─'.repeat(querySentences[0].length + 3)}≪`]]); rl.question(applyLogMargin(_colorText(`;g;│ ;bb;${querySentences.join(`\n${applyLogMargin(';g;│ ;bb;')}`)}\n;g; │\n;x;${applyLogMargin(`;g;└─${promptFormat ?? ''};g;≫ ;y;`)}`)), (answer) => { rs(answer); rl.close(); }); }); } static async promptYesNo(query) { const answer = await this.prompt(`${query}?`, '┤;by;Y;bw;/;by;N;g;├'); return answer.toLowerCase() == 'y' || answer.toLowerCase() == 'yes'; } } function printLog(log) { if (log == null) { return console.log(''); } const [kind] = log; switch (kind) { case '': return console.log(getBasicLog(log), _colorText(';x;')); case 'hl': return console.log(getBorderLog(log), _colorText(';x;')); case 'h1': case 'h2': case 'h3': return console.log(getHeaderLog(log), _colorText(';x;')); case 'p': return console.log(getParagraphLog(log), _colorText(';x;')); case 'py': return console.log(getPropertyLog(log), _colorText(';x;')); case 'e': return console.log(getExampleLog(log), _colorText(';x;')); case 's': return console.log(getSyntaxLog(log), _colorText(';x;')); case 'cd': case 'd': return console.log(getDefinitionLog(log), _colorText(';x;')); default: throw Error(`Invalid Log Kind: "${kind}"`); } } function getBasicLog(log) { const [, message, marginOffset] = log; return applyLogMargin(_colorText(message), _defaultIndent + (marginOffset ?? 0)); } function getBorderLog(log) { const [, colorCode, borderLength, extraMargin] = log; return applyLogMargin(_colorText(`;${colorCode};${'─'.repeat(borderLength)}`), _leftLogMargin + (extraMargin || 0)); } function getHeaderLog(log) { const [kind, header, marginOffset] = log; const [title, text] = header; if (title.length > _maxLogLength || (text && text.length + title.length > _maxLogLength)) { throw Error('headers cannot be larger than max log length'); } const headerColor = kind == 'h1' ? ';bw;' : ';b;'; const styledHeader = kind == 'h1' || kind == 'h2' ? `${kind == 'h1' ? ';ul;' : ''}${headerColor}${title};xx;${headerColor} ;x;${text ?? ''}` : `;bg;... ;bb;${title};xx; ;bg;...;xx;`; return _colorText(applyLogMargin(styledHeader, _leftLogMargin + (marginOffset ?? 0))); } function getParagraphLog(log) { const [, message, marginOffset] = log; return _colorText(createFixedWidthSentences(`;bk;${message}`, _defaultIndent + (marginOffset ?? 0)).join('\n')); } function getPropertyLog(log) { const [, property, margin] = log; const marginOffset = margin ?? 0; const [name, value] = property; const sentences = createFixedWidthSentences(`;bk;${value}`, name.length + 2 + marginOffset + _leftLogMargin); sentences[0] = `;c;${name}: ${sentences[0].trimStart()}`; return _colorText(applyLogMargin(sentences.join('\n'), _leftLogMargin + _defaultIndent + marginOffset)); } function getExampleLog(log) { const [, command] = log; const [cmdName, example] = command; return applyLogMargin(_colorText(`;by;wak;c;${cmdName ? ` -${cmdName}` : ''} ;y;${example}`), _leftLogMargin + _defaultIndent); } function getSyntaxLog(log) { const [, commands, args] = log; const resolvedMessage = commands ? `;by;wak ;bk;[${commands .map((c) => `;c;${c ? ` -${c}` : ''};x;`) .join(' ;bk;|')} ;bk;] ;y;${args};x;` : `;by;wak ;y;${args}`; return applyLogMargin(_colorText(resolvedMessage), _leftLogMargin + _defaultIndent); } function getDefinitionLog(log) { const [kind, message, offset] = log; const [word, definition] = message; const defaultOffset = offset ?? 0; const wordLength = PrinterColor.stripColorCodes(word).length; const [firstSentence, ...leftoverSentences] = createFixedWidthSentences(definition, 0, _maxLogLength - wordLength - 10); const newFirst = applyLogMargin(`${kind == 'cd' ? ';c;-' : ';y;'}${word} ;x;${firstSentence.trim()}`, _leftLogMargin + _defaultIndent + defaultOffset); const paddedSentences = leftoverSentences.map((s) => applyLogMargin(s, wordLength + _defaultIndent * 2 + defaultOffset + (kind == 'cd' ? 1 : 0))); return _colorText([newFirst, ...paddedSentences].join('\n')); } function createFixedWidthSentences(text, margin = 0, logLength = _maxLogLength) { const words = text.replaceAll('\n', ' ').split(' '); const paragraph = []; const stripColors = PrinterColor.stripColorCodes; let sentence = ''; while (words.length) { if (words[0].length > logLength) { throw Error(`Cannot paragraph a word longer than line length: "${words[0]}"`); } const word = stripColors(words[0]); if (word.length + stripColors(sentence).length <= logLength) { sentence += `${words[0]} `; words.splice(0, 1); if (!words.length) { paragraph.push(sentence.trim()); } continue; } paragraph.push(sentence.trim()); sentence = ''; } return paragraph.map((v) => applyLogMargin(v, _leftLogMargin + margin)); } function applyLogMargin(text, leftMargin = _leftLogMargin) { return `${' '.repeat(leftMargin)}${text}`; } function getStackInfo(stack) { const stackLines = stack.split('\n'); if (!stackLines.length || stackLines[1].trim().indexOf('at ') != 0) { throw Error('not a stack trace'); } const hasSourceMap = stack.includes('.ts:'); const pathLines = stackLines.map((l) => l.trim().replace('file:///', '')); pathLines.splice(0, 2); const execLine = pathLines[0]; const offenderFunc = execLine.includes(' (') ? execLine.split(' (')[0].replace('at ', '') : ''; const [path, lineNumber] = execLine .split(hasSourceMap ? ':\\' : ':/')[1] .split(':') .map((v) => v.replace(')', '')); return [offenderFunc, pathBasename(path), lineNumber]; } //# sourceMappingURL=printer.js.map