refactor: move operations into separate module (#4582)

Co-authored-by: Alexandr <qwerty541zxc@gmail.com>
This commit is contained in:
Alexandr Garbuzov
2025-10-15 23:29:53 +03:00
committed by GitHub
co-authored by Alexandr
parent 6d1a5e51bc
commit e158f0d27f
17 changed files with 147 additions and 146 deletions
+2 -1
View File
@@ -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) => {
+2 -1
View File
@@ -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";
+2 -1
View File
@@ -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";
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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";
+2 -1
View File
@@ -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) => {
+1 -1
View File
@@ -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.
*
+1 -2
View File
@@ -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";
+2 -1
View File
@@ -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;
+2 -7
View File
@@ -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;
+2 -1
View File
@@ -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.
+1 -1
View File
@@ -1,6 +1,6 @@
// @ts-check
import { clampValue } from "./utils.js";
import { clampValue } from "./ops.js";
const MIN = 60;
const HOUR = 60 * MIN;
+1 -1
View File
@@ -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.
-6
View File
@@ -10,12 +10,6 @@ export {
ERROR_CARD_LENGTH,
renderError,
encodeHTML,
parseBoolean,
parseArray,
clampValue,
flexLayout,
measureText,
lowercaseTrim,
chunkArray,
parseEmojis,
} from "./utils.js";
+124
View File
@@ -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<T>} arr Array of languages.
* @param {number} perChunk Number of languages per column.
* @returns {Array<T>} 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,
};
-119
View File
@@ -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<T>} arr Array of languages.
* @param {number} perChunk Number of languages per column.
* @returns {Array<T>} 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,
};
+2 -1
View File
@@ -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", () => {