Static Portfolio & Documentation

The many endeavours of Amarnath Patel (jeebuscrossaint)

View on GitHub

Yavafetch

Yavafetch is a simple system information tool written in JavaScript. It is simply made to learn JavaScript as I wanted a simple project that is also kind of detailed and does not use any external libraries. It is inspired by neofetch/fastfetch/pfetch. It is written with as minimal as possible shellcode so I could truly get the terrible experience of JavaScript. It is also incredibly modular and easy to add new features to.

Installation

npm install yavafetch -g

Boom now you have yavafetch installed on your system.

Usage

Just run the command Yavafetch in your terminal and you will see the information about your system.

Documentation

It is MIT licensed, and published on the NPM registry. You can find the source code on GitHub.


index.js (main entry point)

We start off with a bunch of imports from the relative modules of the program. These modules are responsible for getting the information about the system and printing it to the console. They are the actual logic. The main entry point is responsible for calling these functions and printing the information to the console.

const { printAsciiArt } = require('../modules/ascii');
const { printHelp } = require('../modules/help');
const { nameInfo } = require('../modules/nameInfo');
const { osName } = require('../modules/osName');
const { hostName } = require('../modules/host');
const { printOsSpecificAsciiArt } = require('../modules/specificAscii');
const { arch } = require('../modules/arch');
const { kernelVersion } = require('../modules/kernel');
const { getUptime } = require('../modules/uptime');
const { determineManager } = require('../modules/packageInfo');
const { cpuInfo } = require('../modules/cpuInfo');
const { memInfo } = require('../modules/memInfo');
const { swapInfo } = require('../modules/swapInfo');
const { getLocalIP } = require('../modules/network');
const { getGpuInfo } = require('../modules/gpuInfo');
const { getShellInfo } = require('../modules/shell');
const { getBatteryInfo } = require('../modules/battery');
const { listDisks } = require('../modules/disk');

Next in the index.js file we have the asynchronous defaultInfo function which is what executs if the user does not add any flags to the program. It is responsible for calling all the functions that get the system information and printing it to the console.

async function defaultInfo() {
    printOsSpecificAsciiArt();
    const name = nameInfo();
    console.log(`\x1b[1m${name}\x1b[0m`);
    const os = osName();
    console.log(`\x1b[1mOS:\x1b[0m ${os}`);
    const host = hostName();
    console.log(`\x1b[1mHost:\x1b[0m ${host}`); 
    const architecture = arch();
    console.log(`\x1b[1mArchitecture:\x1b[0m ${architecture}`);
    const version = kernelVersion();
    console.log(`\x1b[1mKernel Version:\x1b[0m ${version}`);
    const up = getUptime();
    console.log(`\x1b[1mUptime:\x1b[0m ${up}`);
    const manager = determineManager();
    console.log(`\x1b[1mPackage Manager:\x1b[0m ${manager}`); // this only works on windows
    const cpu = cpuInfo();
    console.log(`\x1b[1mCPU:\x1b[0m ${cpu.slice(4)}`);
    const mem = memInfo();
    console.log(`\x1b[1mMemory:\x1b[0m ${mem}`);
    const swap = swapInfo();
    console.log(`\x1b[1mSwap:\x1b[0m ${swap}`);
    const ip = getLocalIP();
    console.log(`\x1b[1mLocal IP:\x1b[0m ${ip}`);
    const shell = getShellInfo();
    console.log(`\x1b[1mShell:\x1b[0m ${shell}`);
    const battery = await getBatteryInfo();
    console.log(`\x1b[1mBattery:\x1b[0m ${battery}`);
    const disk = listDisks();
    console.log(`${disk}`);
    const gpu = getGpuInfo();
    console.log(`${gpu}`);
}

The last part of the index.js file is the main function which is responsible for parsing the flags and calling the appropriate functions.


async function main() {
    const flags = process.argv.slice(2);
    switch (true) {
        case flags.includes('--help'):
            printHelp();
            break;
        
        case flags.includes('--ascii'):
            printAsciiArt();
            await defaultInfo(); // Add await here
            break;
        default:
            await defaultInfo();
    }
}

main();

arch.js

Incredibly simple code. Just returns the architecture Node identifies.


const os = require('os');

function arch() {
    return os.arch();
}

module.exports = { arch };

ascii.js

Also super simple, simply console logs the ASCII art.


function printAsciiArt() {
    console.log(`
    _____.___.                    _____       __         .__     
    \\__  |   |____ ___  _______ _/ ____\\_____/  |_  ____ |  |__  
     /   |   \\__  \\\\  \\/ /\\__  \\\\   __\\/ __ \\   __\\/ ___\\|  |  \\ 
     \\____   |/ __ \\\\   /  / __ \\|  | \\  ___/|  | \\  \\___|   Y  \\
     / ______(____  /\\_/  (____  /__|  \\___  >__|  \\___  >___|  /
     \\/           \\/  
    `);
}

module.exports = { printAsciiArt };

battery.js

A module that is finally a little more complex. We can get the battery information of the system using some shell code and then parsing it.


const { exec } = require('child_process'); // Importing the exec function from the child_process module

function getBatteryInfo() {
    return new Promise((resolve, reject) => {
        exec('wmic path Win32_Battery get EstimatedChargeRemaining, BatteryStatus', (error, stdout) => { // Executing the command to get battery information
            if (error) {
                reject(`Error: ${error.message}`);
                return;
            }

            const lines = stdout.trim().split('\n'); // Splitting the output into lines
            const batteryInfo = lines[1].trim().split(/\s{2,}/); // Splitting the second line into the battery information

            const chargeRemaining = parseInt(batteryInfo[1]); // Parsing the charge remaining
            const batteryStatus = parseInt(batteryInfo[0]); // Parsing the battery status

            let result;
            if (chargeRemaining >= 0 && batteryStatus !== 0) {
                const status = batteryStatus === 2 ? 'Connected' : 'Disconnected'; // Checking if the battery is connected or disconnected
                result = `${chargeRemaining}% [${status}]`; // Creating the result string of the battery information
            } else {
                result = 'Battery information not available'; // If the battery information is not available
            }

            // Check if result is undefined before resolving
            resolve(result ? result : 'Battery information not available'); // Resolving the promise with the battery information
        });
    });
}

module.exports = { getBatteryInfo };

cpuInfo.js

Simple module like the architecture module. Just returns the CPU information that Node manages to find.


const os = require('os'); // Importing the os module

function cpuInfo() {
    const cpus = os.cpus(); // initializing the cpus object
    const cpuModel = cpus[0].model.replace(/\s+/g, ' ').trim(); // model of the cpu parsed
    const cpuSpeed = cpus[0].speed; // mhz of the cpu
    const cpuCores = cpus.length; // amount of cores
    return `CPU:${cpuModel} @ ${cpuSpeed} MHz (${cpuCores} cores)`;
}

module.exports = { cpuInfo };

disk.js

This file is a little more lenghty and complex. It is still simple, but its about 100 lines so I’m not going to put a lot of code here, rather just talk about it.

So firstly of coures we import exec from child_process and os from node. We then have a function that formats the bytes of a disk which basically counts how many bytes a disk has and then converts it to a human readable format.


const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; // array of reasonable units
    let unit = 0;
    while (bytes >= 1024 && unit < units.length - 1) { // if the amount of bytes is greater than 1024 and the unit is less than the length of the units array
        bytes /= 1024; // divide the bytes by 1024
        unit++; // increment the unit
    }
    return `${bytes.toFixed(2)} ${units[unit]}`; // return the bytes in a human readable format