UNPKG

jkansii

Version:

Easy to use ANSI terminal codes with tons of features

157 lines (135 loc) 4.2 kB
/** * TSize * Efficiently get the terminal size. * * MIT License * Copyright (c) 2022 Battledash-2 */ const version = parseInt(process.version.match(/\d+/)[0]); const avaMax = version < 15; // Nodejs imports let child_process; if (avaMax) child_process = require('child_process'); else child_process = require('node:child_process'); let fs; if (avaMax) fs = require('fs'); else fs = require('node:fs'); // Variables const mod = {}; // easy reference to module.exports const platform = process.platform; // Platform const iswin = platform === 'win32'; // Client is on Windows const defaultValue = Infinity; const state = { width: defaultValue, height: defaultValue }; // Current state const stdout = process.stdout; const comspec = iswin ? 'powershell.exe' : (fs.existsSync('/dev/bash') ? '/dev/bash' : (fs.existsSync('/dev/sh') ? '/dev/sh' : '')); /** * * @param {String} cmd * @param {String[]} arg * @returns {Object} */ const spawn = (cmd, arg) => { let output; if (iswin) { output = child_process.spawnSync( comspec, [cmd, ...arg], { stdio: ['inherit', 'pipe', 'ignore'], // [stdin, stdout, stderr] shell: false } ); } else { if (comspec !== '') { output = child_process.spawnSync( comspec, ['-c', cmd.replace(/"/g, '\\"') + ' ' + arg.join('').replace(/"/g, '\\"')], { stdio: ['inherit', 'pipe', 'ignore'], shell: false } ); } else { output = child_process.spawnSync( cmd, arg, { stdio: ['inherit', 'pipe', 'ignore'] } ); } } output.output = [null, String(output[1]), null]; output.stdout = String(output.stdout).replace('\r\n', ''); output.stderr = String(output.stderr).replace('\r\n', ''); return output; } /** * @description Default values */ const getSizeReadOnly = () => ({ width: defaultValue, height: defaultValue, }); /** * @description Gets the terminal size for unknown devices. Returns { width, height } * @param {ReadStream} std * @returns {Object} */ const getSizeUnknown = (std=stdout) => { if (typeof std.height !== 'undefined' && typeof std.width !== 'undefined') return { width: std.width, height: std.height }; else { let output = spawn('stty', ['size']); if (typeof output.error !== 'undefined' || output.status !== 0) output = spawn('echo', ['"$LINES $COLUMNS"']); if (typeof output.error !== 'undefined' || output.status !== 0) output = spawn('echo', ['"$(tput lines) $(tput cols)"']); if (typeof output.error !== 'undefined' || output.status !== 0) return getSizeReadOnly(); const size = output.stdout.split(' ').map(c=>parseInt(c)); return { width: size[1], height: size[0], }; } } /** * @description Gets the terminal size for windows devices. Returns { width, height } * @param {ReadStream} std * @returns {Object} */ const getSizeWin = (std=stdout) => { if (typeof std.height !== 'undefined' && typeof std.width !== 'undefined') return { width: std.width, height: std.height }; else { const output = spawn('echo', ["(($Host.UI.RawUI.WindowSize.Width).toString() + 'x' + ($Host.UI.RawUI.WindowSize.Height).toString())"]); if (typeof output.error !== 'undefined' || output.status !== 0) return getSizeReadOnly(); const size = output.stdout.split('x').map(c=>parseInt(c)); return { width: size[0], height: size[1], }; } } /** * @param {Object} object * @returns {Object} Object[NaN=Infinity] */ const preventNaN = (object={}) => { for (let property in object) { if (isNaN(object[property])) object[property] = Infinity; } return object; } /** * @description Gets the terminal size. Returns { width, height } * @param {ReadStream} std * @returns {Object} */ const getSize = (std=stdout) => { if (iswin) return preventNaN(getSizeWin(std)); return preventNaN(getSizeUnknown(std)); } mod.size = ()=>preventNaN(getSize()); mod.getLines = (string='') => { let a = Math.floor(string.toString().length / mod.size().width) - 1; let m = string.match(/\n/g); let b = m != null ? m.length : 0; return a+b; } module.exports = mod;