mirror of
https://github.com/System-End/github-readme-stats.git
synced 2026-04-19 19:55:16 +00:00
refactor: jsdoc in utils & minor changes (#1377)
* refactor: jsdoc in utils & minor changes * chore: jsdoc Card class * chore: jsdoc for getStyles
This commit is contained in:
parent
d0ab2ff030
commit
02ebd3243b
7 changed files with 143 additions and 41 deletions
|
|
@ -34,8 +34,6 @@ module.exports = async (req, res) => {
|
||||||
border_radius,
|
border_radius,
|
||||||
border_color,
|
border_color,
|
||||||
} = req.query;
|
} = req.query;
|
||||||
let stats;
|
|
||||||
|
|
||||||
res.setHeader("Content-Type", "image/svg+xml");
|
res.setHeader("Content-Type", "image/svg+xml");
|
||||||
|
|
||||||
if (blacklist.includes(username)) {
|
if (blacklist.includes(username)) {
|
||||||
|
|
@ -47,7 +45,7 @@ module.exports = async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stats = await fetchStats(
|
const stats = await fetchStats(
|
||||||
username,
|
username,
|
||||||
parseBoolean(count_private),
|
parseBoolean(count_private),
|
||||||
parseBoolean(include_all_commits),
|
parseBoolean(include_all_commits),
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ module.exports = async (req, res) => {
|
||||||
border_color,
|
border_color,
|
||||||
} = req.query;
|
} = req.query;
|
||||||
|
|
||||||
let repoData;
|
|
||||||
|
|
||||||
res.setHeader("Content-Type", "image/svg+xml");
|
res.setHeader("Content-Type", "image/svg+xml");
|
||||||
|
|
||||||
if (blacklist.includes(username)) {
|
if (blacklist.includes(username)) {
|
||||||
|
|
@ -40,7 +38,7 @@ module.exports = async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
repoData = await fetchRepo(username, repo);
|
const repoData = await fetchRepo(username, repo);
|
||||||
|
|
||||||
let cacheSeconds = clampValue(
|
let cacheSeconds = clampValue(
|
||||||
parseInt(cache_seconds || CONSTANTS.TWO_HOURS, 10),
|
parseInt(cache_seconds || CONSTANTS.TWO_HOURS, 10),
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ module.exports = async (req, res) => {
|
||||||
border_radius,
|
border_radius,
|
||||||
border_color,
|
border_color,
|
||||||
} = req.query;
|
} = req.query;
|
||||||
let topLangs;
|
|
||||||
|
|
||||||
res.setHeader("Content-Type", "image/svg+xml");
|
res.setHeader("Content-Type", "image/svg+xml");
|
||||||
|
|
||||||
if (blacklist.includes(username)) {
|
if (blacklist.includes(username)) {
|
||||||
|
|
@ -44,7 +42,7 @@ module.exports = async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
topLangs = await fetchTopLanguages(
|
const topLangs = await fetchTopLanguages(
|
||||||
username,
|
username,
|
||||||
parseArray(exclude_repo),
|
parseArray(exclude_repo),
|
||||||
parseArray(hide),
|
parseArray(hide),
|
||||||
|
|
|
||||||
|
|
@ -110,15 +110,14 @@ const renderRepoCard = (repo, options = {}) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// returns theme based colors with proper overrides and defaults
|
// returns theme based colors with proper overrides and defaults
|
||||||
const { titleColor, textColor, iconColor, bgColor, borderColor } =
|
const colors = getCardColors({
|
||||||
getCardColors({
|
title_color,
|
||||||
title_color,
|
icon_color,
|
||||||
icon_color,
|
text_color,
|
||||||
text_color,
|
bg_color,
|
||||||
bg_color,
|
border_color,
|
||||||
border_color,
|
theme,
|
||||||
theme,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const svgLanguage = primaryLanguage
|
const svgLanguage = primaryLanguage
|
||||||
? createLanguageNode(langName, langColor)
|
? createLanguageNode(langName, langColor)
|
||||||
|
|
@ -145,22 +144,16 @@ const renderRepoCard = (repo, options = {}) => {
|
||||||
width: 400,
|
width: 400,
|
||||||
height,
|
height,
|
||||||
border_radius,
|
border_radius,
|
||||||
colors: {
|
colors,
|
||||||
titleColor,
|
|
||||||
textColor,
|
|
||||||
iconColor,
|
|
||||||
bgColor,
|
|
||||||
borderColor,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
card.disableAnimations();
|
card.disableAnimations();
|
||||||
card.setHideBorder(hide_border);
|
card.setHideBorder(hide_border);
|
||||||
card.setHideTitle(false);
|
card.setHideTitle(false);
|
||||||
card.setCSS(`
|
card.setCSS(`
|
||||||
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
|
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${colors.textColor} }
|
||||||
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
|
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${colors.textColor} }
|
||||||
.icon { fill: ${iconColor} }
|
.icon { fill: ${colors.iconColor} }
|
||||||
.badge { font: 600 11px 'Segoe UI', Ubuntu, Sans-Serif; }
|
.badge { font: 600 11px 'Segoe UI', Ubuntu, Sans-Serif; }
|
||||||
.badge rect { opacity: 0.2 }
|
.badge rect { opacity: 0.2 }
|
||||||
`);
|
`);
|
||||||
|
|
@ -168,9 +161,9 @@ const renderRepoCard = (repo, options = {}) => {
|
||||||
return card.render(`
|
return card.render(`
|
||||||
${
|
${
|
||||||
isTemplate
|
isTemplate
|
||||||
? getBadgeSVG(i18n.t("repocard.template"), textColor)
|
? getBadgeSVG(i18n.t("repocard.template"), colors.textColor)
|
||||||
: isArchived
|
: isArchived
|
||||||
? getBadgeSVG(i18n.t("repocard.archived"), textColor)
|
? getBadgeSVG(i18n.t("repocard.archived"), colors.textColor)
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,16 @@ const { getAnimations } = require("../getStyles");
|
||||||
const { flexLayout, encodeHTML } = require("../common/utils");
|
const { flexLayout, encodeHTML } = require("../common/utils");
|
||||||
|
|
||||||
class Card {
|
class Card {
|
||||||
|
/**
|
||||||
|
* @param {object} args
|
||||||
|
* @param {number?=} args.width
|
||||||
|
* @param {number?=} args.height
|
||||||
|
* @param {number?=} args.border_radius
|
||||||
|
* @param {string?=} args.customTitle
|
||||||
|
* @param {string?=} args.defaultTitle
|
||||||
|
* @param {string?=} args.titlePrefixIcon
|
||||||
|
* @param {ReturnType<import('../common/utils').getCardColors>?=} args.colors
|
||||||
|
*/
|
||||||
constructor({
|
constructor({
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
|
|
@ -38,14 +48,23 @@ class Card {
|
||||||
this.animations = false;
|
this.animations = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
setCSS(value) {
|
setCSS(value) {
|
||||||
this.css = value;
|
this.css = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} value
|
||||||
|
*/
|
||||||
setHideBorder(value) {
|
setHideBorder(value) {
|
||||||
this.hideBorder = value;
|
this.hideBorder = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} value
|
||||||
|
*/
|
||||||
setHideTitle(value) {
|
setHideTitle(value) {
|
||||||
this.hideTitle = value;
|
this.hideTitle = value;
|
||||||
if (value) {
|
if (value) {
|
||||||
|
|
@ -53,6 +72,9 @@ class Card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
setTitle(text) {
|
setTitle(text) {
|
||||||
this.title = text;
|
this.title = text;
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +136,9 @@ class Card {
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} body
|
||||||
|
*/
|
||||||
render(body) {
|
render(body) {
|
||||||
return `
|
return `
|
||||||
<svg
|
<svg
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
|
// @ts-check
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const wrap = require("word-wrap");
|
const wrap = require("word-wrap");
|
||||||
const themes = require("../../themes");
|
const themes = require("../../themes");
|
||||||
const toEmoji = require("emoji-name-map");
|
const toEmoji = require("emoji-name-map");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} message
|
||||||
|
* @param {string} secondaryMessage
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
const renderError = (message, secondaryMessage = "") => {
|
const renderError = (message, secondaryMessage = "") => {
|
||||||
return `
|
return `
|
||||||
<svg width="495" height="120" viewBox="0 0 495 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="495" height="120" viewBox="0 0 495 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
@ -21,7 +27,11 @@ const renderError = (message, secondaryMessage = "") => {
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://stackoverflow.com/a/48073476/10629172
|
/**
|
||||||
|
* @see https://stackoverflow.com/a/48073476/10629172
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
function encodeHTML(str) {
|
function encodeHTML(str) {
|
||||||
return str
|
return str
|
||||||
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
||||||
|
|
@ -30,18 +40,29 @@ function encodeHTML(str) {
|
||||||
.replace(/\u0008/gim, "");
|
.replace(/\u0008/gim, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} num
|
||||||
|
*/
|
||||||
function kFormatter(num) {
|
function kFormatter(num) {
|
||||||
return Math.abs(num) > 999
|
return Math.abs(num) > 999
|
||||||
? Math.sign(num) * (Math.abs(num) / 1000).toFixed(1) + "k"
|
? Math.sign(num) * parseFloat((Math.abs(num) / 1000).toFixed(1)) + "k"
|
||||||
: Math.sign(num) * Math.abs(num);
|
: Math.sign(num) * Math.abs(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} hexColor
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
function isValidHexColor(hexColor) {
|
function isValidHexColor(hexColor) {
|
||||||
return new RegExp(
|
return new RegExp(
|
||||||
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/,
|
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/,
|
||||||
).test(hexColor);
|
).test(hexColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} value
|
||||||
|
* @returns {boolean | string}
|
||||||
|
*/
|
||||||
function parseBoolean(value) {
|
function parseBoolean(value) {
|
||||||
if (value === "true") {
|
if (value === "true") {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -52,19 +73,37 @@ function parseBoolean(value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
*/
|
||||||
function parseArray(str) {
|
function parseArray(str) {
|
||||||
if (!str) return [];
|
if (!str) return [];
|
||||||
return str.split(",");
|
return str.split(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} number
|
||||||
|
* @param {number} min
|
||||||
|
* @param {number} max
|
||||||
|
*/
|
||||||
function clampValue(number, min, max) {
|
function clampValue(number, min, max) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (Number.isNaN(parseInt(number))) return min;
|
||||||
return Math.max(min, Math.min(number, max));
|
return Math.max(min, Math.min(number, max));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]} colors
|
||||||
|
*/
|
||||||
function isValidGradient(colors) {
|
function isValidGradient(colors) {
|
||||||
return isValidHexColor(colors[1]) && isValidHexColor(colors[2]);
|
return isValidHexColor(colors[1]) && isValidHexColor(colors[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} color
|
||||||
|
* @param {string} fallbackColor
|
||||||
|
* @returns {string | string[]}
|
||||||
|
*/
|
||||||
function fallbackColor(color, fallbackColor) {
|
function fallbackColor(color, fallbackColor) {
|
||||||
let colors = color.split(",");
|
let colors = color.split(",");
|
||||||
let gradient = null;
|
let gradient = null;
|
||||||
|
|
@ -79,7 +118,12 @@ function fallbackColor(color, fallbackColor) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('axios').AxiosRequestConfig['data']} data
|
||||||
|
* @param {import('axios').AxiosRequestConfig['headers']} headers
|
||||||
|
*/
|
||||||
function request(data, headers) {
|
function request(data, headers) {
|
||||||
|
// @ts-ignore
|
||||||
return axios({
|
return axios({
|
||||||
url: "https://api.github.com/graphql",
|
url: "https://api.github.com/graphql",
|
||||||
method: "post",
|
method: "post",
|
||||||
|
|
@ -92,8 +136,8 @@ function request(data, headers) {
|
||||||
* @param {object} props
|
* @param {object} props
|
||||||
* @param {string[]} props.items
|
* @param {string[]} props.items
|
||||||
* @param {number} props.gap
|
* @param {number} props.gap
|
||||||
* @param {number[]} props.sizes
|
* @param {number[]?=} props.sizes
|
||||||
* @param {"column" | "row"} props.direction
|
* @param {"column" | "row"?=} props.direction
|
||||||
*
|
*
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*
|
*
|
||||||
|
|
@ -115,14 +159,27 @@ function flexLayout({ items, gap, direction, sizes = [] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns theme based colors with proper overrides and defaults
|
/**
|
||||||
|
* @typedef {object} CardColors
|
||||||
|
* @prop {string} title_color
|
||||||
|
* @prop {string} text_color
|
||||||
|
* @prop {string} icon_color
|
||||||
|
* @prop {string} bg_color
|
||||||
|
* @prop {string} border_color
|
||||||
|
* @prop {keyof typeof import('../../themes')?=} fallbackTheme
|
||||||
|
* @prop {keyof typeof import('../../themes')?=} theme
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* returns theme based colors with proper overrides and defaults
|
||||||
|
* @param {CardColors} options
|
||||||
|
*/
|
||||||
function getCardColors({
|
function getCardColors({
|
||||||
title_color,
|
title_color,
|
||||||
text_color,
|
text_color,
|
||||||
icon_color,
|
icon_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
theme,
|
|
||||||
border_color,
|
border_color,
|
||||||
|
theme,
|
||||||
fallbackTheme = "default",
|
fallbackTheme = "default",
|
||||||
}) {
|
}) {
|
||||||
const defaultTheme = themes[fallbackTheme];
|
const defaultTheme = themes[fallbackTheme];
|
||||||
|
|
@ -157,6 +214,12 @@ function getCardColors({
|
||||||
return { titleColor, iconColor, textColor, bgColor, borderColor };
|
return { titleColor, iconColor, textColor, bgColor, borderColor };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} maxLines
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
function wrapTextMultiline(text, width = 60, maxLines = 3) {
|
function wrapTextMultiline(text, width = 60, maxLines = 3) {
|
||||||
const wrapped = wrap(encodeHTML(text), { width })
|
const wrapped = wrap(encodeHTML(text), { width })
|
||||||
.split("\n") // Split wrapped lines to get an array of lines
|
.split("\n") // Split wrapped lines to get an array of lines
|
||||||
|
|
@ -193,6 +256,10 @@ const SECONDARY_ERROR_MESSAGES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class CustomError extends Error {
|
class CustomError extends Error {
|
||||||
|
/**
|
||||||
|
* @param {string} message
|
||||||
|
* @param {string} type
|
||||||
|
*/
|
||||||
constructor(message, type) {
|
constructor(message, type) {
|
||||||
super(message);
|
super(message);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
@ -203,7 +270,12 @@ class CustomError extends Error {
|
||||||
static USER_NOT_FOUND = "USER_NOT_FOUND";
|
static USER_NOT_FOUND = "USER_NOT_FOUND";
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/a/48172630/10629172
|
/**
|
||||||
|
* @see https://stackoverflow.com/a/48172630/10629172
|
||||||
|
* @param {string} str
|
||||||
|
* @param {number} fontSize
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function measureText(str, fontSize = 10) {
|
function measureText(str, fontSize = 10) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const widths = [
|
const widths = [
|
||||||
|
|
@ -237,6 +309,8 @@ function measureText(str, fontSize = 10) {
|
||||||
.reduce((cur, acc) => acc + cur) * fontSize
|
.reduce((cur, acc) => acc + cur) * fontSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {string} name */
|
||||||
const lowercaseTrim = (name) => name.toLowerCase().trim();
|
const lowercaseTrim = (name) => name.toLowerCase().trim();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
|
/**
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
const calculateCircleProgress = (value) => {
|
const calculateCircleProgress = (value) => {
|
||||||
let radius = 40;
|
const radius = 40;
|
||||||
let c = Math.PI * (radius * 2);
|
const c = Math.PI * (radius * 2);
|
||||||
|
|
||||||
if (value < 0) value = 0;
|
if (value < 0) value = 0;
|
||||||
if (value > 100) value = 100;
|
if (value > 100) value = 100;
|
||||||
|
|
||||||
let percentage = ((100 - value) / 100) * c;
|
return ((100 - value) / 100) * c;
|
||||||
return percentage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {{progress: number}} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
const getProgressAnimation = ({ progress }) => {
|
const getProgressAnimation = ({ progress }) => {
|
||||||
return `
|
return `
|
||||||
@keyframes rankAnimation {
|
@keyframes rankAnimation {
|
||||||
|
|
@ -44,6 +51,15 @@ const getAnimations = () => {
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{
|
||||||
|
* titleColor: string;
|
||||||
|
* textColor: string;
|
||||||
|
* iconColor: string;
|
||||||
|
* show_icons: boolean;
|
||||||
|
* progress: number;
|
||||||
|
* }} args
|
||||||
|
*/
|
||||||
const getStyles = ({
|
const getStyles = ({
|
||||||
titleColor,
|
titleColor,
|
||||||
textColor,
|
textColor,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue