Skip to content

Commit

Permalink
feat: config file (#71)
Browse files Browse the repository at this point in the history
* feat: add a possibility of providing a config file

* fixes

* added simple tests

* improved link in readme

* link-fix

* x
  • Loading branch information
golota60 committed Sep 26, 2021
1 parent 4b09744 commit bacae30
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 61 deletions.
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,36 @@ Yayfetch is a tool similar to screenfetch, it just displays info about your comp

`--rgb r,g,b` - specify RGB values in which data will be shown. Cannot be used with -c(--color) flag. Example `npx yayfetch --rgb 125,25,78`

`--hide-logo` - prints data without ASCII art
`--no-logo` - prints data without ASCII art

`--custom-lines <{key: string; value: string;}>[]` - array of objects with {key, value} string pairs separated by spaces ex.
`'{key: 'Funny:', value: 'joke'}' '{key: 'Even funnier:', value: 'joke'}' <...>`. This is being parsed using JSON.parse, so if you encounter any problem, make sure that string you provided can be parsed by it.
`--custom-lines {[key]: value, [key2]: value2, ...}` - object with {[key]: value} string pairs separated by spaces ex.
`'{"Funny:": "joke", "exampleline:": "examplevalue"}'`. This is being parsed using JSON.parse, so if you encounter any problem, make sure that string you provided can be parsed by it.

`--no-colored-boxes` - hides the colored boxes underneath the information.

`-h` or `--help` - shows available flags.

`--config <path_to_file>` - specify a file path to a custom config. See [here](#example-config)

More features to come!


## Example config

You can specify options through a file and use them by using `--config <path_to_file>`. Config file should contain a JSON object with keys representing flags.

Note that every flag with a prefix of `--no-` just negates the flag that is on by default. For example CLI flag `--no-colored-boxes` negates `colored-boxes` flag, which is `true` by default. This is important for creating a config, because if you want to invoke `--no-colored-boxes` through config, you would provide a `"colored-boxes": false` in JSON object.

Example config:
```
{
"color": "blue",
"colored-boxes": false,
"logo": false,
"custom-lines": {"Funny:": "joke", "exampleline:": "examplevalue"}
}
```

## It doesn't work!

If it doesn't work for you make sure that you have the newest node(it's developed using node 14.17.0, although it should work with everything >=8.6)
Expand Down
30 changes: 21 additions & 9 deletions src/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os from "os";
import { promises } from "fs";
import { exit } from "process";
import chalk from "chalk";
import { RGBColors } from "../interfaces/general";

Expand Down Expand Up @@ -37,7 +39,7 @@ export const parseRGBStringToNumber = (rgbString: string): RGBColors => {
});
if (split.length !== 3) {
throw new Error(
"Specified RGB color was provided in incorrect form. Please remember that there has to be exactly 3 colors, they need to be separated by comas and numbers must be between 0 and 255"
"Specified RGB color was provided in incorrect form. Please remember that there has to be exactly 3 colors, separated by comas, and numbers have to be between 0 and 255"
);
}

Expand Down Expand Up @@ -197,8 +199,10 @@ export const returnColoredText = (
};

export const printInColumns = (...cols: string[]): void => {
// Split every line by \n
// First element is the logo, second one is the data, each of which has lines separated by \n
// Splitting those creates a string[][] where the elements of nested arrays are lines
const colsSplit = cols.map((element) => element.split("\n"));

// Find the vertically longest argument
// Length of which is going the be the iterator on how many times we have to print a line
const verticallyLongestArg = colsSplit.reduce(
Expand All @@ -210,9 +214,7 @@ export const printInColumns = (...cols: string[]): void => {
for (const [i] of [...new Array(verticallyLongestArg)].entries()) {
let nextLine = "";
for (const [i2] of [...new Array(argsNumber)].entries()) {
if (nextLine === "") {
colsSplit[i2];
}
if (!colsSplit[i2][i]) continue;

nextLine += colsSplit[i2][i];
}
Expand All @@ -226,9 +228,9 @@ export const printInColumns = (...cols: string[]): void => {

export const printData = (
{ logo, data }: { logo: string; data: string },
hideLogo = false
showLogo = true
): void => {
if (hideLogo) {
if (!showLogo) {
console.log(data);
} else {
printInColumns(logo, data);
Expand Down Expand Up @@ -281,14 +283,14 @@ export const getColoredBoxes = () => {
)}`;
};

export const yayfetchASCII = `-/-\` \`-//-\` \`-/-
export const yayfetchASCII = `-/-\` \`-//-\` \`-/-
-////\` .//////. \`////-
\`/////\` \`:////////:\` \`/////\`
\`/////\` ./////:://///. \`/////\`
\`/////\` -/////. ./////- \`/////\`
./////--://///- -/////:-://///.
./////////////====:////////////.
./////////:=======://///////.
./////////:=======://////////.
.///////:\` \`:///////.
.//////\` ://///.
./////\` \`/////.
Expand All @@ -298,3 +300,13 @@ export const yayfetchASCII = `-/-\` \`-//-\` \`-/-
.////: :////.
-////. .////.
.:/. .:\\. `;

export const handleReadFile = async (path: string): Promise<any> => {
try {
const file = await promises.readFile(path, "utf-8");
return JSON.parse(file);
} catch (err) {
console.error(`Error when reading file: ${err}`);
exit();
}
};
36 changes: 21 additions & 15 deletions src/helpers/systeminformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const getDisplaysAndGraphicsCards =
gpuInfo,
displays,
};
} catch {
} catch (err) {
console.error(`Error when reading graphics info: ${err}`);
return {
gpuInfo: [errorMessage],
displays: [errorMessage],
Expand All @@ -64,7 +65,8 @@ export const getMemoryInfo = async (): Promise<MemoryInfoInterface> => {
used: used.toFixed(0),
total: total.toFixed(0),
};
} catch {
} catch (err) {
console.error(`Error when reading memory info: ${err}`);
return {
free: errorMessage,
used: "",
Expand All @@ -86,44 +88,48 @@ export const getOsInfo = async (): Promise<string> => {
export const getShellInfo = async (): Promise<string> => {
try {
return await sysinf.shell();
} catch {
} catch (shellErr) {
if (os.platform() === "win32") {
// Windows doesn't support .shell() feature
try {
const osInfo: sysinf.Systeminformation.UserData[] =
await sysinf.users();
return osInfo ? osInfo[0].tty : "";
} catch {
} catch (err) {
console.error(`Error when reading windows's terminal info: ${err}`);
return errorMessage;
}
} else {
console.error(`Error when reading shell info: ${shellErr}`);
return errorMessage;
}
}
};

export const getSysInfOsInfo = async (): Promise<
OSInfoInterface | undefined
> => {
export const getSysInfOsInfo = async (): Promise<OSInfoInterface> => {
try {
const osInfo = await sysinf.osInfo();
return {
distro: osInfo.distro,
hostname: osInfo.hostname,
display: `${osInfo.codename} ${osInfo.release} ${osInfo.build} ${osInfo.arch}`,
};
} catch (error: unknown) {
console.error(error);
} catch (err) {
console.error(`Error when reading OS info: ${err}`);
return {
distro: errorMessage,
hostname: "",
display: "",
};
}
};

export const getHWInfo = async (): Promise<
sysinf.Systeminformation.SystemData | undefined
> => {
export const getHWInfo = async (): Promise<string> => {
try {
const hwInfo = await sysinf.system();
return hwInfo;
} catch (error: unknown) {
console.error(error);
return hwInfo.model;
} catch (err) {
console.error(`Error when reading hardware info: ${err}`);
return errorMessage;
}
};
71 changes: 38 additions & 33 deletions src/yayfetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
import os from "os";
import yargs from "yargs";
import inquirer from "inquirer";
import { Systeminformation } from "systeminformation";
import {
DisplayAndGraphicsCard,
MemoryInfoInterface,
OSInfoInterface,
} from "./interfaces/systeminformation";
import {
uptimeInMinutes,
Expand All @@ -18,6 +16,7 @@ import {
availableColors,
PredefinedColors,
getColoredBoxes,
handleReadFile,
} from "./helpers/helpers";
import {
getEndianness,
Expand Down Expand Up @@ -75,8 +74,8 @@ async function returnPickedData(
color: PredefinedColors | RGBColors
): Promise<string[]> {
const allData: SystemInformation = await getSystemInformation();
const sysinfOsInfo = (await getSysInfOsInfo()) ?? ({} as OSInfoInterface);
const hwInfo = (await getHWInfo()) ?? ({} as Systeminformation.SystemData);
const sysinfOsInfo = await getSysInfOsInfo();
const hwInfo = await getHWInfo();
const pickedVals = [
`${returnColoredText(
`${allData.osInfo.username}@${sysinfOsInfo.hostname}`,
Expand All @@ -97,9 +96,7 @@ async function returnPickedData(
}

if (valuesToDisplay.includes("Model")) {
pickedVals.push(
`${returnColoredText("Model:", color, true)} ${hwInfo.model}`
);
pickedVals.push(`${returnColoredText("Model:", color, true)} ${hwInfo}`);
}

if (valuesToDisplay.includes("Release")) {
Expand Down Expand Up @@ -169,27 +166,33 @@ async function returnPickedData(
return pickedVals;
}

/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-implicit-any-catch */
const _args = yargs
yargs
.command("$0", "", async () => {
try {
if (yargs.argv.color && yargs.argv.rgb) {
const configFilePath = yargs.argv["config"] as string;
let configFile;
if (configFilePath)
configFile = (await handleReadFile(configFilePath)) || [];
const enhancedArgv = { ...yargs.argv, ...configFile };

if (enhancedArgv.color && enhancedArgv.rgb) {
throw new Error(
"--rgb and --color are mutually exclusive - please specify only one"
);
}

const predefinedColor = String(yargs.argv.c || yargs.argv.color);
const customColors = yargs.argv.rgb
? parseRGBStringToNumber(String(yargs.argv.rgb))
const predefinedColor = String(enhancedArgv.c || enhancedArgv.color);
const customColors = enhancedArgv.rgb
? parseRGBStringToNumber(String(enhancedArgv.rgb))
: false;

const colorToUse = customColors || predefinedColor;
const hideLogoFlag = Boolean(yargs.argv["hide-logo"]);
const coloredBoxes = Boolean(yargs.argv["colored-boxes"]);
const customLines = yargs.argv["custom-lines"] as string[];
const showLogo = Boolean(enhancedArgv["logo"]);
const coloredBoxes = Boolean(enhancedArgv["colored-boxes"]);
const customLines: string | object = enhancedArgv["custom-lines"];

/* Get device data */
let infoToPrint: string[];
if (yargs.argv.p || yargs.argv.pick) {
if (enhancedArgv.p || enhancedArgv.pick) {
const inquirerPrompt = await inquirer.prompt<{
displayedValues: string[];
}>([promptQuestions]);
Expand All @@ -204,18 +207,17 @@ const _args = yargs
);
}

/* Add custom lines if specified */
if (customLines) {
const customLinesParsed = customLines.map((line) =>
JSON.parse(line)
) as Array<{
key: string;
value: string;
}>;
const customLinesParsed =
typeof customLines === "object"
? customLines
: JSON.parse(customLines);
infoToPrint = [
...infoToPrint,
...customLinesParsed.map((customLine) => {
return `${returnColoredText(customLine.key, colorToUse, true)} ${
customLine.value
...Object.entries(customLinesParsed).map((customLine) => {
return `${returnColoredText(customLine[0], colorToUse, true)} ${
customLine[1]
}`;
}),
];
Expand All @@ -231,7 +233,7 @@ const _args = yargs
logo: returnColoredText(yayfetchASCII, colorToUse),
data: infoToPrint.join("\n"),
},
hideLogoFlag
showLogo
);
} catch (error) {
console.error(`‼️ ${error} ‼️`);
Expand All @@ -251,26 +253,29 @@ const _args = yargs
choices: availableColors,
type: "string",
})
.option("hide-logo", {
.option("logo", {
describe: "Hides the ASCII logo",
type: "boolean",
default: false,
default: true,
})
.option("rgb", {
describe: "Same as color, but you provide r,g,b values ex. 128,5,67",
type: "string",
})
.option("custom-lines", {
describe: "Array of lines you want to add(objects with key, value pairs)",
type: "array",
describe: "String object of key-value pairs to add",
type: "string",
})
.option("colored-boxes", {
describe: "Hides colored boxes underneath the information",
type: "boolean",
default: true,
})
.option("config", {
describe: "Specify a file path to a config file",
type: "string",
})
.help()
.version()
.alias("help", "h")
.example("npx yayfetch", "Returns information about your system").argv;
/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-implicit-any-catch */
7 changes: 7 additions & 0 deletions tests/__tests__/testfile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

{
"color": "blue",
"colored-boxes": false,
"logo": false,
"custom-lines": {"Funny:": "joke", "exampleline:": "examplevalue"}
}
18 changes: 17 additions & 1 deletion tests/__tests__/yayfetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,27 @@ describe("yayfetch", () => {
});
it("prints custom lines", async () => {
const output = await getYayfetchOutput(
`--custom-lines '{"key": "Funny", "value": "joke"}'`
`--custom-lines '{"Funny:": "joke"}'`
);
const baseFlagsEnhanced = [...baseFlags, "Funny"];
baseFlagsEnhanced.forEach((val) => {
expect(output.stdout).toContain(val);
});
});
it("prints multiple custom lines", async() => {
const output = await getYayfetchOutput(
`--custom-lines '{"Funny:": "joke", "Funnier": "joke"}'`
);
const baseFlagsEnhanced = [...baseFlags, "Funny", "Funnier"];
baseFlagsEnhanced.forEach((val) => {
expect(output.stdout).toContain(val);
});
})
it('reads config from file', async () => {
const output = await getYayfetchOutput('--config tests/__tests__/testfile.json');
const baseFlagsEnhanced = [...baseFlags, "Funny:", "exampleline:"];
baseFlagsEnhanced.forEach((val) => {
expect(output.stdout).toContain(val);
});
})
});

0 comments on commit bacae30

Please sign in to comment.