diff --git a/api/gist.js b/api/gist.js index 88f2f7b2..7edbd676 100644 --- a/api/gist.js +++ b/api/gist.js @@ -1,6 +1,6 @@ // @ts-check -import { renderError, parseBoolean } from "../src/common/utils.js"; +import { renderError } from "../src/common/utils.js"; import { isLocaleAvailable } from "../src/translations.js"; import { renderGistCard } from "../src/cards/gist.js"; import { fetchGist } from "../src/fetchers/gist.js"; @@ -15,6 +15,7 @@ import { MissingParamError, retrieveSecondaryMessage, } from "../src/common/error.js"; +import { parseBoolean } from "../src/common/ops.js"; // @ts-ignore export default async (req, res) => { diff --git a/api/index.js b/api/index.js index f353d4c7..d6d6514e 100644 --- a/api/index.js +++ b/api/index.js @@ -12,7 +12,8 @@ import { MissingParamError, retrieveSecondaryMessage, } from "../src/common/error.js"; -import { parseArray, parseBoolean, renderError } from "../src/common/utils.js"; +import { parseArray, parseBoolean } from "../src/common/ops.js"; +import { renderError } from "../src/common/utils.js"; import { fetchStats } from "../src/fetchers/stats.js"; import { isLocaleAvailable } from "../src/translations.js"; diff --git a/api/pin.js b/api/pin.js index 62b06a88..ccadb803 100644 --- a/api/pin.js +++ b/api/pin.js @@ -12,7 +12,8 @@ import { MissingParamError, retrieveSecondaryMessage, } from "../src/common/error.js"; -import { parseBoolean, renderError } from "../src/common/utils.js"; +import { parseBoolean } from "../src/common/ops.js"; +import { renderError } from "../src/common/utils.js"; import { fetchRepo } from "../src/fetchers/repo.js"; import { isLocaleAvailable } from "../src/translations.js"; diff --git a/api/status/pat-info.js b/api/status/pat-info.js index 87cdc6e4..db3a8cc5 100644 --- a/api/status/pat-info.js +++ b/api/status/pat-info.js @@ -9,7 +9,7 @@ import { request } from "../../src/common/http.js"; import { logger } from "../../src/common/log.js"; -import { dateDiff } from "../../src/common/utils.js"; +import { dateDiff } from "../../src/common/ops.js"; export const RATE_LIMIT_SECONDS = 60 * 5; // 1 request per 5 minutes diff --git a/api/top-langs.js b/api/top-langs.js index c00fde6b..a2733918 100644 --- a/api/top-langs.js +++ b/api/top-langs.js @@ -12,7 +12,8 @@ import { MissingParamError, retrieveSecondaryMessage, } from "../src/common/error.js"; -import { parseArray, parseBoolean, renderError } from "../src/common/utils.js"; +import { parseArray, parseBoolean } from "../src/common/ops.js"; +import { renderError } from "../src/common/utils.js"; import { fetchTopLanguages } from "../src/fetchers/top-languages.js"; import { isLocaleAvailable } from "../src/translations.js"; diff --git a/api/wakatime.js b/api/wakatime.js index af6c4af0..f88eeebf 100644 --- a/api/wakatime.js +++ b/api/wakatime.js @@ -1,7 +1,7 @@ // @ts-check import { renderWakatimeCard } from "../src/cards/wakatime.js"; -import { parseArray, parseBoolean, renderError } from "../src/common/utils.js"; +import { renderError } from "../src/common/utils.js"; import { fetchWakatimeStats } from "../src/fetchers/wakatime.js"; import { isLocaleAvailable } from "../src/translations.js"; import { @@ -15,6 +15,7 @@ import { MissingParamError, retrieveSecondaryMessage, } from "../src/common/error.js"; +import { parseArray, parseBoolean } from "../src/common/ops.js"; // @ts-ignore export default async (req, res) => { diff --git a/src/cards/gist.js b/src/cards/gist.js index 2deaf448..2db6782f 100644 --- a/src/cards/gist.js +++ b/src/cards/gist.js @@ -1,7 +1,6 @@ // @ts-check import { - parseEmojis, encodeHTML, measureText, flexLayout, @@ -12,6 +11,7 @@ import Card from "../common/Card.js"; import { getCardColors } from "../common/color.js"; import { kFormatter, wrapTextMultiline } from "../common/fmt.js"; import { icons } from "../common/icons.js"; +import { parseEmojis } from "../common/ops.js"; /** Import language colors. * diff --git a/src/cards/repo.js b/src/cards/repo.js index ccf42645..fbc23231 100644 --- a/src/cards/repo.js +++ b/src/cards/repo.js @@ -5,14 +5,13 @@ import { getCardColors } from "../common/color.js"; import { kFormatter, wrapTextMultiline } from "../common/fmt.js"; import { I18n } from "../common/I18n.js"; import { icons } from "../common/icons.js"; +import { clampValue, parseEmojis } from "../common/ops.js"; import { encodeHTML, flexLayout, measureText, - parseEmojis, iconWithLabel, createLanguageNode, - clampValue, } from "../common/utils.js"; import { repoCardLocales } from "../translations.js"; diff --git a/src/cards/stats.js b/src/cards/stats.js index 9e6725b0..3120afb5 100644 --- a/src/cards/stats.js +++ b/src/cards/stats.js @@ -6,7 +6,8 @@ import { CustomError } from "../common/error.js"; import { kFormatter } from "../common/fmt.js"; import { I18n } from "../common/I18n.js"; import { icons, rankIcon } from "../common/icons.js"; -import { clampValue, flexLayout, measureText } from "../common/utils.js"; +import { clampValue } from "../common/ops.js"; +import { flexLayout, measureText } from "../common/utils.js"; import { statCardLocales, wakatimeCardLocales } from "../translations.js"; const CARD_MIN_WIDTH = 287; diff --git a/src/cards/top-languages.js b/src/cards/top-languages.js index ec91fcc9..7d4ac977 100644 --- a/src/cards/top-languages.js +++ b/src/cards/top-languages.js @@ -5,13 +5,8 @@ import { getCardColors } from "../common/color.js"; import { createProgressNode } from "../common/createProgressNode.js"; import { formatBytes } from "../common/fmt.js"; import { I18n } from "../common/I18n.js"; -import { - chunkArray, - clampValue, - flexLayout, - lowercaseTrim, - measureText, -} from "../common/utils.js"; +import { chunkArray, clampValue, lowercaseTrim } from "../common/ops.js"; +import { flexLayout, measureText } from "../common/utils.js"; import { langCardLocales } from "../translations.js"; const DEFAULT_CARD_WIDTH = 300; diff --git a/src/cards/wakatime.js b/src/cards/wakatime.js index b1accd45..687750e1 100644 --- a/src/cards/wakatime.js +++ b/src/cards/wakatime.js @@ -4,7 +4,8 @@ import { Card } from "../common/Card.js"; import { getCardColors } from "../common/color.js"; import { createProgressNode } from "../common/createProgressNode.js"; import { I18n } from "../common/I18n.js"; -import { clampValue, flexLayout, lowercaseTrim } from "../common/utils.js"; +import { clampValue, lowercaseTrim } from "../common/ops.js"; +import { flexLayout } from "../common/utils.js"; import { wakatimeCardLocales } from "../translations.js"; /** Import language colors. diff --git a/src/common/cache.js b/src/common/cache.js index 596aa5e3..dc6217e2 100644 --- a/src/common/cache.js +++ b/src/common/cache.js @@ -1,6 +1,6 @@ // @ts-check -import { clampValue } from "./utils.js"; +import { clampValue } from "./ops.js"; const MIN = 60; const HOUR = 60 * MIN; diff --git a/src/common/createProgressNode.js b/src/common/createProgressNode.js index 22a7ad65..e63944e7 100644 --- a/src/common/createProgressNode.js +++ b/src/common/createProgressNode.js @@ -1,6 +1,6 @@ // @ts-check -import { clampValue } from "./utils.js"; +import { clampValue } from "./ops.js"; /** * Create a node to indicate progress in percentage along a horizontal line. diff --git a/src/common/index.js b/src/common/index.js index 84638a6f..12fe563b 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -10,12 +10,6 @@ export { ERROR_CARD_LENGTH, renderError, encodeHTML, - parseBoolean, - parseArray, - clampValue, flexLayout, measureText, - lowercaseTrim, - chunkArray, - parseEmojis, } from "./utils.js"; diff --git a/src/common/ops.js b/src/common/ops.js new file mode 100644 index 00000000..b4db6e60 --- /dev/null +++ b/src/common/ops.js @@ -0,0 +1,124 @@ +// @ts-check + +import toEmoji from "emoji-name-map"; + +/** + * Returns boolean if value is either "true" or "false" else the value as it is. + * + * @param {string | boolean} value The value to parse. + * @returns {boolean | undefined } The parsed value. + */ +const parseBoolean = (value) => { + if (typeof value === "boolean") { + return value; + } + + if (typeof value === "string") { + if (value.toLowerCase() === "true") { + return true; + } else if (value.toLowerCase() === "false") { + return false; + } + } + return undefined; +}; + +/** + * Parse string to array of strings. + * + * @param {string} str The string to parse. + * @returns {string[]} The array of strings. + */ +const parseArray = (str) => { + if (!str) { + return []; + } + return str.split(","); +}; + +/** + * Clamp the given number between the given range. + * + * @param {number} number The number to clamp. + * @param {number} min The minimum value. + * @param {number} max The maximum value. + * @returns {number} The clamped number. + */ +const clampValue = (number, min, max) => { + // @ts-ignore + if (Number.isNaN(parseInt(number, 10))) { + return min; + } + return Math.max(min, Math.min(number, max)); +}; + +/** + * Lowercase and trim string. + * + * @param {string} name String to lowercase and trim. + * @returns {string} Lowercased and trimmed string. + */ +const lowercaseTrim = (name) => name.toLowerCase().trim(); + +/** + * Split array of languages in two columns. + * + * @template T Language object. + * @param {Array} arr Array of languages. + * @param {number} perChunk Number of languages per column. + * @returns {Array} Array of languages split in two columns. + */ +const chunkArray = (arr, perChunk) => { + return arr.reduce((resultArray, item, index) => { + const chunkIndex = Math.floor(index / perChunk); + + if (!resultArray[chunkIndex]) { + // @ts-ignore + resultArray[chunkIndex] = []; // start a new chunk + } + + // @ts-ignore + resultArray[chunkIndex].push(item); + + return resultArray; + }, []); +}; + +/** + * Parse emoji from string. + * + * @param {string} str String to parse emoji from. + * @returns {string} String with emoji parsed. + */ +const parseEmojis = (str) => { + if (!str) { + throw new Error("[parseEmoji]: str argument not provided"); + } + return str.replace(/:\w+:/gm, (emoji) => { + return toEmoji.get(emoji) || ""; + }); +}; + +/** + * Get diff in minutes between two dates. + * + * @param {Date} d1 First date. + * @param {Date} d2 Second date. + * @returns {number} Number of minutes between the two dates. + */ +const dateDiff = (d1, d2) => { + const date1 = new Date(d1); + const date2 = new Date(d2); + const diff = date1.getTime() - date2.getTime(); + return Math.round(diff / (1000 * 60)); +}; + +export { + parseBoolean, + parseArray, + clampValue, + lowercaseTrim, + chunkArray, + parseEmojis, + dateDiff, +}; diff --git a/src/common/utils.js b/src/common/utils.js index 28e6774a..dd3d2402 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -1,6 +1,5 @@ // @ts-check -import toEmoji from "emoji-name-map"; import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js"; import { getCardColors } from "./color.js"; @@ -74,56 +73,6 @@ const iconWithLabel = (icon, label, testid, iconSize) => { return flexLayout({ items: [iconSvg, text], gap: 20 }).join(""); }; -/** - * Returns boolean if value is either "true" or "false" else the value as it is. - * - * @param {string | boolean} value The value to parse. - * @returns {boolean | undefined } The parsed value. - */ -const parseBoolean = (value) => { - if (typeof value === "boolean") { - return value; - } - - if (typeof value === "string") { - if (value.toLowerCase() === "true") { - return true; - } else if (value.toLowerCase() === "false") { - return false; - } - } - return undefined; -}; - -/** - * Parse string to array of strings. - * - * @param {string} str The string to parse. - * @returns {string[]} The array of strings. - */ -const parseArray = (str) => { - if (!str) { - return []; - } - return str.split(","); -}; - -/** - * Clamp the given number between the given range. - * - * @param {number} number The number to clamp. - * @param {number} min The minimum value. - * @param {number} max The maximum value. - * @returns {number} The clamped number. - */ -const clampValue = (number, min, max) => { - // @ts-ignore - if (Number.isNaN(parseInt(number, 10))) { - return min; - } - return Math.max(min, Math.min(number, max)); -}; - // Script parameters. const ERROR_CARD_LENGTH = 576.5; @@ -253,80 +202,12 @@ const measureText = (str, fontSize = 10) => { ); }; -/** - * Lowercase and trim string. - * - * @param {string} name String to lowercase and trim. - * @returns {string} Lowercased and trimmed string. - */ -const lowercaseTrim = (name) => name.toLowerCase().trim(); - -/** - * Split array of languages in two columns. - * - * @template T Language object. - * @param {Array} arr Array of languages. - * @param {number} perChunk Number of languages per column. - * @returns {Array} Array of languages split in two columns. - */ -const chunkArray = (arr, perChunk) => { - return arr.reduce((resultArray, item, index) => { - const chunkIndex = Math.floor(index / perChunk); - - if (!resultArray[chunkIndex]) { - // @ts-ignore - resultArray[chunkIndex] = []; // start a new chunk - } - - // @ts-ignore - resultArray[chunkIndex].push(item); - - return resultArray; - }, []); -}; - -/** - * Parse emoji from string. - * - * @param {string} str String to parse emoji from. - * @returns {string} String with emoji parsed. - */ -const parseEmojis = (str) => { - if (!str) { - throw new Error("[parseEmoji]: str argument not provided"); - } - return str.replace(/:\w+:/gm, (emoji) => { - return toEmoji.get(emoji) || ""; - }); -}; - -/** - * Get diff in minutes between two dates. - * - * @param {Date} d1 First date. - * @param {Date} d2 Second date. - * @returns {number} Number of minutes between the two dates. - */ -const dateDiff = (d1, d2) => { - const date1 = new Date(d1); - const date2 = new Date(d2); - const diff = date1.getTime() - date2.getTime(); - return Math.round(diff / (1000 * 60)); -}; - export { ERROR_CARD_LENGTH, renderError, createLanguageNode, iconWithLabel, encodeHTML, - parseBoolean, - parseArray, - clampValue, flexLayout, measureText, - lowercaseTrim, - chunkArray, - parseEmojis, - dateDiff, }; diff --git a/tests/utils.test.js b/tests/utils.test.js index 0b37e45a..a079841c 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -3,7 +3,8 @@ import { describe, expect, it } from "@jest/globals"; import { queryByTestId } from "@testing-library/dom"; import "@testing-library/jest-dom"; -import { encodeHTML, parseBoolean, renderError } from "../src/common/utils.js"; +import { encodeHTML, renderError } from "../src/common/utils.js"; +import { parseBoolean } from "../src/common/ops.js"; describe("Test utils.js", () => { it("should test parseBoolean", () => {