From 3cf087b160442ab32227892e92194b58fbe9f634 Mon Sep 17 00:00:00 2001 From: Alexandr Garbuzov <186095128+alexandr-garbuzov@users.noreply.github.com> Date: Tue, 14 Oct 2025 21:11:23 +0300 Subject: [PATCH] refactor: move wrap text multiline function into fmt module (#4572) Co-authored-by: Alexandr --- backend/src/cards/gist.js | 3 +- backend/src/cards/repo.js | 3 +- backend/src/common/fmt.js | 42 ++++++++++++++++++++++++++- backend/src/common/index.js | 1 - backend/src/common/utils.js | 37 ----------------------- backend/src/fetchers/stats.js | 2 +- backend/src/fetchers/top-languages.js | 2 +- backend/tests/utils.test.js | 9 ++---- 8 files changed, 48 insertions(+), 51 deletions(-) diff --git a/backend/src/cards/gist.js b/backend/src/cards/gist.js index b55b5d8c..f8e7ab72 100644 --- a/backend/src/cards/gist.js +++ b/backend/src/cards/gist.js @@ -2,7 +2,6 @@ import { parseEmojis, - wrapTextMultiline, encodeHTML, measureText, flexLayout, @@ -11,7 +10,7 @@ import { } from "../common/utils.js"; import Card from "../common/Card.js"; import { getCardColors } from "../common/color.js"; -import { kFormatter } from "../common/fmt.js"; +import { kFormatter, wrapTextMultiline } from "../common/fmt.js"; import { icons } from "../common/icons.js"; import languageColors from "../common/languageColors.json" with { type: "json" }; diff --git a/backend/src/cards/repo.js b/backend/src/cards/repo.js index 1ad6adcf..fe58926b 100644 --- a/backend/src/cards/repo.js +++ b/backend/src/cards/repo.js @@ -2,7 +2,7 @@ import { Card } from "../common/Card.js"; import { getCardColors } from "../common/color.js"; -import { kFormatter } from "../common/fmt.js"; +import { kFormatter, wrapTextMultiline } from "../common/fmt.js"; import { I18n } from "../common/I18n.js"; import { icons } from "../common/icons.js"; import { @@ -10,7 +10,6 @@ import { flexLayout, measureText, parseEmojis, - wrapTextMultiline, iconWithLabel, createLanguageNode, clampValue, diff --git a/backend/src/common/fmt.js b/backend/src/common/fmt.js index 2d40378a..ca86f3cf 100644 --- a/backend/src/common/fmt.js +++ b/backend/src/common/fmt.js @@ -1,3 +1,8 @@ +// @ts-check + +import wrap from "word-wrap"; +import { encodeHTML } from "./utils.js"; + /** * Retrieves num with suffix k(thousands) precise to given decimal places. * @@ -47,4 +52,39 @@ const formatBytes = (bytes) => { return `${(bytes / Math.pow(base, i)).toFixed(1)} ${sizes[i]}`; }; -export { kFormatter, formatBytes }; +/** + * Split text over multiple lines based on the card width. + * + * @param {string} text Text to split. + * @param {number} width Line width in number of characters. + * @param {number} maxLines Maximum number of lines. + * @returns {string[]} Array of lines. + */ +const wrapTextMultiline = (text, width = 59, maxLines = 3) => { + const fullWidthComma = ","; + const encoded = encodeHTML(text); + const isChinese = encoded.includes(fullWidthComma); + + let wrapped = []; + + if (isChinese) { + wrapped = encoded.split(fullWidthComma); // Chinese full punctuation + } else { + wrapped = wrap(encoded, { + width, + }).split("\n"); // Split wrapped lines to get an array of lines + } + + const lines = wrapped.map((line) => line.trim()).slice(0, maxLines); // Only consider maxLines lines + + // Add "..." to the last line if the text exceeds maxLines + if (wrapped.length > maxLines) { + lines[maxLines - 1] += "..."; + } + + // Remove empty lines if text fits in less than maxLines lines + const multiLineText = lines.filter(Boolean); + return multiLineText; +}; + +export { kFormatter, formatBytes, wrapTextMultiline }; diff --git a/backend/src/common/index.js b/backend/src/common/index.js index 93b50cc0..3a61fead 100644 --- a/backend/src/common/index.js +++ b/backend/src/common/index.js @@ -15,7 +15,6 @@ export { clampValue, request, flexLayout, - wrapTextMultiline, logger, measureText, lowercaseTrim, diff --git a/backend/src/common/utils.js b/backend/src/common/utils.js index 66dd4c87..a60754b3 100644 --- a/backend/src/common/utils.js +++ b/backend/src/common/utils.js @@ -2,7 +2,6 @@ import axios from "axios"; import toEmoji from "emoji-name-map"; -import wrap from "word-wrap"; import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js"; import { getCardColors } from "./color.js"; @@ -248,41 +247,6 @@ const renderError = ({ `; }; -/** - * Split text over multiple lines based on the card width. - * - * @param {string} text Text to split. - * @param {number} width Line width in number of characters. - * @param {number} maxLines Maximum number of lines. - * @returns {string[]} Array of lines. - */ -const wrapTextMultiline = (text, width = 59, maxLines = 3) => { - const fullWidthComma = ","; - const encoded = encodeHTML(text); - const isChinese = encoded.includes(fullWidthComma); - - let wrapped = []; - - if (isChinese) { - wrapped = encoded.split(fullWidthComma); // Chinese full punctuation - } else { - wrapped = wrap(encoded, { - width, - }).split("\n"); // Split wrapped lines to get an array of lines - } - - const lines = wrapped.map((line) => line.trim()).slice(0, maxLines); // Only consider maxLines lines - - // Add "..." to the last line if the text exceeds maxLines - if (wrapped.length > maxLines) { - lines[maxLines - 1] += "..."; - } - - // Remove empty lines if text fits in less than maxLines lines - const multiLineText = lines.filter(Boolean); - return multiLineText; -}; - const noop = () => {}; // return console instance based on the environment const logger = @@ -433,7 +397,6 @@ export { buildSearchFilter, request, flexLayout, - wrapTextMultiline, logger, OWNER_AFFILIATIONS, measureText, diff --git a/backend/src/fetchers/stats.js b/backend/src/fetchers/stats.js index 42421448..9b817cca 100644 --- a/backend/src/fetchers/stats.js +++ b/backend/src/fetchers/stats.js @@ -9,11 +9,11 @@ import { buildSearchFilter, logger, request, - wrapTextMultiline, parseOwnerAffiliations, } from "../common/utils.js"; import { excludeRepositories } from "../common/envs.js"; import { CustomError, MissingParamError } from "../common/error.js"; +import { wrapTextMultiline } from "../common/fmt.js"; dotenv.config(); diff --git a/backend/src/fetchers/top-languages.js b/backend/src/fetchers/top-languages.js index 5b57bd5c..9abcbf46 100644 --- a/backend/src/fetchers/top-languages.js +++ b/backend/src/fetchers/top-languages.js @@ -4,11 +4,11 @@ import { retryer } from "../common/retryer.js"; import { logger, request, - wrapTextMultiline, parseOwnerAffiliations, } from "../common/utils.js"; import { excludeRepositories } from "../common/envs.js"; import { CustomError, MissingParamError } from "../common/error.js"; +import { wrapTextMultiline } from "../common/fmt.js"; /** * @typedef {import("axios").AxiosRequestHeaders} AxiosRequestHeaders Axios request headers. diff --git a/backend/tests/utils.test.js b/backend/tests/utils.test.js index 9be0ae2f..92b12f7c 100644 --- a/backend/tests/utils.test.js +++ b/backend/tests/utils.test.js @@ -3,12 +3,8 @@ import { describe, expect, it } from "@jest/globals"; import { queryByTestId } from "@testing-library/dom"; import "@testing-library/jest-dom"; -import { - encodeHTML, - parseBoolean, - renderError, - wrapTextMultiline, -} from "../src/common/utils.js"; +import { encodeHTML, parseBoolean, renderError } from "../src/common/utils.js"; +import { wrapTextMultiline } from "../src/common/fmt.js"; describe("Test utils.js", () => { it("should test parseBoolean", () => { @@ -25,6 +21,7 @@ describe("Test utils.js", () => { expect(parseBoolean("1")).toBe(undefined); expect(parseBoolean("0")).toBe(undefined); expect(parseBoolean("")).toBe(undefined); + // @ts-ignore expect(parseBoolean(undefined)).toBe(undefined); });