forked from mirrored/github-readme-stats
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1da12ad80 | ||
|
|
6c79c2d82d | ||
|
|
4f1fa61b25 |
+15
@@ -0,0 +1,15 @@
|
|||||||
|
import statsCard from './api/index.js'
|
||||||
|
import repoCard from './api/pin.js'
|
||||||
|
import langCard from './api/top-langs.js'
|
||||||
|
import wakatimeCard from './api/wakatime.js'
|
||||||
|
import express from 'express'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
const app = express()
|
||||||
|
app.listen(process.env.port || 9000)
|
||||||
|
|
||||||
|
app.get('/', statsCard)
|
||||||
|
app.get('/pin', repoCard)
|
||||||
|
app.get('/top-langs', langCard)
|
||||||
|
app.get('/wakatime', wakatimeCard)
|
||||||
@@ -92,7 +92,9 @@ Visit <https://indiafightscorona.giveindia.org> and make a small donation to hel
|
|||||||
- [Repo Card Exclusive Options](#repo-card-exclusive-options)
|
- [Repo Card Exclusive Options](#repo-card-exclusive-options)
|
||||||
- [Language Card Exclusive Options](#language-card-exclusive-options)
|
- [Language Card Exclusive Options](#language-card-exclusive-options)
|
||||||
- [Wakatime Card Exclusive Option](#wakatime-card-exclusive-options)
|
- [Wakatime Card Exclusive Option](#wakatime-card-exclusive-options)
|
||||||
- [Deploy Yourself](#deploy-on-your-own-vercel-instance)
|
- [Deploy Yourself](#deploy-on-your-own)
|
||||||
|
- [On Vercel](#on-vercel)
|
||||||
|
- [On other platforms](#on-other-platforms)
|
||||||
- [Keep your fork up to date](#keep-your-fork-up-to-date)
|
- [Keep your fork up to date](#keep-your-fork-up-to-date)
|
||||||
|
|
||||||
# GitHub Stats Card
|
# GitHub Stats Card
|
||||||
@@ -509,7 +511,9 @@ By default, GitHub does not lay out the cards side by side. To do that, you can
|
|||||||
</a>
|
</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploy on your own Vercel instance
|
## Deploy on your own
|
||||||
|
|
||||||
|
### On Vercel
|
||||||
|
|
||||||
#### :film_projector: [Check Out Step By Step Video Tutorial By @codeSTACKr](https://youtu.be/n6d4KHSKqGk?t=107)
|
#### :film_projector: [Check Out Step By Step Video Tutorial By @codeSTACKr](https://youtu.be/n6d4KHSKqGk?t=107)
|
||||||
|
|
||||||
@@ -546,6 +550,23 @@ Since the GitHub API only allows 5k requests per hour, my `https://github-readme
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### On other platforms
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> This way of using GRS is not officially supported and was added to cater to some particular use cases where Vercel could not be used (e.g. #2341). The support for this method, therefore, is limited.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>:hammer_and_wrench: Step-by-step guide for deploying on other platforms</b></summary>
|
||||||
|
|
||||||
|
1. Fork or clone this repo as per your needs
|
||||||
|
2. Add `express` to the dependencies section of `package.json`
|
||||||
|
https://github.com/anuraghazra/github-readme-stats/blob/ba7c2f8b55eac8452e479c8bd38b044d204d0424/package.json#L54-L61
|
||||||
|
3. Run `npm i` if needed (initial setup)
|
||||||
|
4. Run `node express.js` to start the server, or set the entry point to `express.js` in `package.json` if you're deploying on a managed service
|
||||||
|
https://github.com/anuraghazra/github-readme-stats/blob/ba7c2f8b55eac8452e479c8bd38b044d204d0424/package.json#L11
|
||||||
|
5. You're done 🎉
|
||||||
|
</details>
|
||||||
|
|
||||||
### Keep your fork up to date
|
### Keep your fork up to date
|
||||||
|
|
||||||
You can keep your fork, and thus your private Vercel instance up to date with the upstream using GitHubs' [Sync Fork button](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). You can also use the [pull](https://github.com/wei/pull) package created by [@wei](https://github.com/wei) to automate this process.
|
You can keep your fork, and thus your private Vercel instance up to date with the upstream using GitHubs' [Sync Fork button](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). You can also use the [pull](https://github.com/wei/pull) package created by [@wei](https://github.com/wei) to automate this process.
|
||||||
|
|||||||
+35
-51
@@ -87,6 +87,16 @@ const iconWithLabel = (icon, label, testid) => {
|
|||||||
* @returns {string} Repository card SVG object.
|
* @returns {string} Repository card SVG object.
|
||||||
*/
|
*/
|
||||||
const renderRepoCard = (repo, options = {}) => {
|
const renderRepoCard = (repo, options = {}) => {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
nameWithOwner,
|
||||||
|
description,
|
||||||
|
primaryLanguage,
|
||||||
|
isArchived,
|
||||||
|
isTemplate,
|
||||||
|
starCount,
|
||||||
|
forkCount,
|
||||||
|
} = repo;
|
||||||
const {
|
const {
|
||||||
hide_border = false,
|
hide_border = false,
|
||||||
title_color,
|
title_color,
|
||||||
@@ -99,42 +109,6 @@ const renderRepoCard = (repo, options = {}) => {
|
|||||||
border_color,
|
border_color,
|
||||||
locale,
|
locale,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const i18n = new I18n({
|
|
||||||
locale,
|
|
||||||
translations: repoCardLocales,
|
|
||||||
});
|
|
||||||
|
|
||||||
const colors = getCardColors({
|
|
||||||
title_color,
|
|
||||||
icon_color,
|
|
||||||
text_color,
|
|
||||||
bg_color,
|
|
||||||
border_color,
|
|
||||||
theme,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
nameWithOwner,
|
|
||||||
description,
|
|
||||||
primaryLanguage,
|
|
||||||
isArchived,
|
|
||||||
isTemplate,
|
|
||||||
starCount,
|
|
||||||
forkCount,
|
|
||||||
} = repo;
|
|
||||||
|
|
||||||
const highlight = repo.highlight
|
|
||||||
? // @ts-ignore
|
|
||||||
getBadgeSVG(repo.highlight, colors.textColor)
|
|
||||||
: isTemplate
|
|
||||||
? // @ts-ignore
|
|
||||||
getBadgeSVG(i18n.t("repocard.template"), colors.textColor)
|
|
||||||
: isArchived
|
|
||||||
? // @ts-ignore
|
|
||||||
getBadgeSVG(i18n.t("repocard.archived"), colors.textColor)
|
|
||||||
: ""
|
|
||||||
|
|
||||||
const lineHeight = 10;
|
const lineHeight = 10;
|
||||||
const header = show_owner ? nameWithOwner : name;
|
const header = show_owner ? nameWithOwner : name;
|
||||||
@@ -151,25 +125,27 @@ const renderRepoCard = (repo, options = {}) => {
|
|||||||
const height =
|
const height =
|
||||||
(descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight;
|
(descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight;
|
||||||
|
|
||||||
|
const i18n = new I18n({
|
||||||
|
locale,
|
||||||
|
translations: repoCardLocales,
|
||||||
|
});
|
||||||
|
|
||||||
// returns theme based colors with proper overrides and defaults
|
// returns theme based colors with proper overrides and defaults
|
||||||
|
const colors = getCardColors({
|
||||||
|
title_color,
|
||||||
|
icon_color,
|
||||||
|
text_color,
|
||||||
|
bg_color,
|
||||||
|
border_color,
|
||||||
|
theme,
|
||||||
|
});
|
||||||
|
|
||||||
const svgLanguage = primaryLanguage
|
const svgLanguage = primaryLanguage
|
||||||
? createLanguageNode(langName, langColor)
|
? createLanguageNode(langName, langColor)
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
let totalStars
|
const totalStars = kFormatter(starCount);
|
||||||
let totalForks
|
const totalForks = kFormatter(forkCount);
|
||||||
if (isNaN(starCount)) {
|
|
||||||
totalStars = starCount;
|
|
||||||
} else {
|
|
||||||
totalStars = kFormatter(starCount);
|
|
||||||
}
|
|
||||||
if (isNaN(forkCount)) {
|
|
||||||
totalForks = forkCount;
|
|
||||||
} else {
|
|
||||||
totalForks = kFormatter(forkCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
const svgStars = iconWithLabel(icons.star, totalStars, "stargazers");
|
const svgStars = iconWithLabel(icons.star, totalStars, "stargazers");
|
||||||
const svgForks = iconWithLabel(icons.fork, totalForks, "forkcount");
|
const svgForks = iconWithLabel(icons.fork, totalForks, "forkcount");
|
||||||
|
|
||||||
@@ -204,7 +180,15 @@ const renderRepoCard = (repo, options = {}) => {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
return card.render(`
|
return card.render(`
|
||||||
${highlight}
|
${
|
||||||
|
isTemplate
|
||||||
|
? // @ts-ignore
|
||||||
|
getBadgeSVG(i18n.t("repocard.template"), colors.textColor)
|
||||||
|
: isArchived
|
||||||
|
? // @ts-ignore
|
||||||
|
getBadgeSVG(i18n.t("repocard.archived"), colors.textColor)
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
<text class="description" x="25" y="-5">
|
<text class="description" x="25" y="-5">
|
||||||
${descriptionSvg}
|
${descriptionSvg}
|
||||||
|
|||||||
+32
-44
@@ -40,12 +40,7 @@ const createTextNode = ({
|
|||||||
shiftValuePos,
|
shiftValuePos,
|
||||||
bold,
|
bold,
|
||||||
}) => {
|
}) => {
|
||||||
let kValue
|
const kValue = kFormatter(value);
|
||||||
if (isNaN(value)) {
|
|
||||||
kValue = value;
|
|
||||||
} else {
|
|
||||||
kValue = kFormatter(value);
|
|
||||||
}
|
|
||||||
const staggerDelay = (index + 3) * 150;
|
const staggerDelay = (index + 3) * 150;
|
||||||
|
|
||||||
const labelOffset = showIcons ? `x="25"` : "";
|
const labelOffset = showIcons ? `x="25"` : "";
|
||||||
@@ -80,6 +75,15 @@ const createTextNode = ({
|
|||||||
* @returns {string} The stats card SVG object.
|
* @returns {string} The stats card SVG object.
|
||||||
*/
|
*/
|
||||||
const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
totalStars,
|
||||||
|
totalCommits,
|
||||||
|
totalIssues,
|
||||||
|
totalPRs,
|
||||||
|
contributedTo,
|
||||||
|
rank,
|
||||||
|
} = stats;
|
||||||
const {
|
const {
|
||||||
hide = [],
|
hide = [],
|
||||||
show_icons = false,
|
show_icons = false,
|
||||||
@@ -102,37 +106,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
locale,
|
locale,
|
||||||
disable_animations = false,
|
disable_animations = false,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const name = stats.name || "GitHub User"
|
|
||||||
|
|
||||||
const apostrophe = ["x", "s"].includes(name.slice(-1).toLocaleLowerCase())
|
|
||||||
? ""
|
|
||||||
: "s";
|
|
||||||
|
|
||||||
const i18n = new I18n({
|
|
||||||
locale,
|
|
||||||
translations: statCardLocales({ name, apostrophe }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
totalStars = "2018",
|
|
||||||
totalCommits = "29019",
|
|
||||||
totalIssues = "203",
|
|
||||||
totalPRs = "127",
|
|
||||||
contributedTo = "232",
|
|
||||||
rank = {
|
|
||||||
level: "S",
|
|
||||||
score: 10
|
|
||||||
},
|
|
||||||
starsTitle = i18n.t("statcard.totalstars"),
|
|
||||||
commitsTitle = `${i18n.t("statcard.commits")}${
|
|
||||||
include_all_commits ? "" : ` (${new Date().getFullYear()})`
|
|
||||||
}`,
|
|
||||||
issuesTitle = i18n.t("statcard.issues"),
|
|
||||||
PRsTitle = i18n.t("statcard.prs"),
|
|
||||||
contribsTitle = i18n.t("statcard.contribs") + " (last year)",
|
|
||||||
title = i18n.t("statcard.title"),
|
|
||||||
} = stats;
|
|
||||||
|
|
||||||
const lheight = parseInt(String(line_height), 10);
|
const lheight = parseInt(String(line_height), 10);
|
||||||
|
|
||||||
@@ -148,35 +121,45 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
theme,
|
theme,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const apostrophe = ["x", "s"].includes(name.slice(-1).toLocaleLowerCase())
|
||||||
|
? ""
|
||||||
|
: "s";
|
||||||
|
const i18n = new I18n({
|
||||||
|
locale,
|
||||||
|
translations: statCardLocales({ name, apostrophe }),
|
||||||
|
});
|
||||||
|
|
||||||
// Meta data for creating text nodes with createTextNode function
|
// Meta data for creating text nodes with createTextNode function
|
||||||
const STATS = {
|
const STATS = {
|
||||||
stars: {
|
stars: {
|
||||||
icon: icons.star,
|
icon: icons.star,
|
||||||
label: starsTitle,
|
label: i18n.t("statcard.totalstars"),
|
||||||
value: totalStars,
|
value: totalStars,
|
||||||
id: "stars",
|
id: "stars",
|
||||||
},
|
},
|
||||||
commits: {
|
commits: {
|
||||||
icon: icons.commits,
|
icon: icons.commits,
|
||||||
label: commitsTitle,
|
label: `${i18n.t("statcard.commits")}${
|
||||||
|
include_all_commits ? "" : ` (${new Date().getFullYear()})`
|
||||||
|
}`,
|
||||||
value: totalCommits,
|
value: totalCommits,
|
||||||
id: "commits",
|
id: "commits",
|
||||||
},
|
},
|
||||||
prs: {
|
prs: {
|
||||||
icon: icons.prs,
|
icon: icons.prs,
|
||||||
label: PRsTitle,
|
label: i18n.t("statcard.prs"),
|
||||||
value: totalPRs,
|
value: totalPRs,
|
||||||
id: "prs",
|
id: "prs",
|
||||||
},
|
},
|
||||||
issues: {
|
issues: {
|
||||||
icon: icons.issues,
|
icon: icons.issues,
|
||||||
label: issuesTitle,
|
label: i18n.t("statcard.issues"),
|
||||||
value: totalIssues,
|
value: totalIssues,
|
||||||
id: "issues",
|
id: "issues",
|
||||||
},
|
},
|
||||||
contribs: {
|
contribs: {
|
||||||
icon: icons.contribs,
|
icon: icons.contribs,
|
||||||
label: contribsTitle,
|
label: i18n.t("statcard.contribs") + " (last year)",
|
||||||
value: contributedTo,
|
value: contributedTo,
|
||||||
id: "contribs",
|
id: "contribs",
|
||||||
},
|
},
|
||||||
@@ -232,7 +215,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const calculateTextWidth = () => {
|
const calculateTextWidth = () => {
|
||||||
return measureText(custom_title ? custom_title : title);
|
return measureText(custom_title ? custom_title : i18n.t("statcard.title"));
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -258,7 +241,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
|
|
||||||
const card = new Card({
|
const card = new Card({
|
||||||
customTitle: custom_title,
|
customTitle: custom_title,
|
||||||
defaultTitle: title,
|
defaultTitle: i18n.t("statcard.title"),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
border_radius,
|
border_radius,
|
||||||
@@ -323,6 +306,11 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
const labels = Object.keys(STATS)
|
const labels = Object.keys(STATS)
|
||||||
.filter((key) => !hide.includes(key))
|
.filter((key) => !hide.includes(key))
|
||||||
.map((key) => {
|
.map((key) => {
|
||||||
|
if (key === "commits") {
|
||||||
|
return `${i18n.t("statcard.commits")} ${
|
||||||
|
include_all_commits ? "" : `in ${new Date().getFullYear()}`
|
||||||
|
} : ${totalStars}`;
|
||||||
|
}
|
||||||
return `${STATS[key].label}: ${STATS[key].value}`;
|
return `${STATS[key].label}: ${STATS[key].value}`;
|
||||||
})
|
})
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ const getLongestLang = (arr) =>
|
|||||||
* @param {number} props.index Index of the programming language.
|
* @param {number} props.index Index of the programming language.
|
||||||
* @returns {string} Programming language SVG node.
|
* @returns {string} Programming language SVG node.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const createProgressTextNode = ({ width, color, name, progress, index }) => {
|
const createProgressTextNode = ({ width, color, name, progress, index }) => {
|
||||||
const staggerDelay = (index + 3) * 150;
|
const staggerDelay = (index + 3) * 150;
|
||||||
const paddingRight = 95;
|
const paddingRight = 95;
|
||||||
@@ -57,7 +56,7 @@ const createProgressTextNode = ({ width, color, name, progress, index }) => {
|
|||||||
return `
|
return `
|
||||||
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
||||||
<text data-testid="lang-name" x="2" y="15" class="lang-name">${name}</text>
|
<text data-testid="lang-name" x="2" y="15" class="lang-name">${name}</text>
|
||||||
<text x="${progressTextX}" y="34" class="lang-name">${text || `${progress}%`}</text>
|
<text x="${progressTextX}" y="34" class="lang-name">${progress}%</text>
|
||||||
${createProgressNode({
|
${createProgressNode({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 25,
|
y: 25,
|
||||||
@@ -90,7 +89,7 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => {
|
|||||||
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
||||||
<circle cx="5" cy="6" r="5" fill="${color}" />
|
<circle cx="5" cy="6" r="5" fill="${color}" />
|
||||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
||||||
${lang.name} ${hideProgress ? "" : (lang.text || `${percentage}%`)}
|
${lang.name} ${hideProgress ? "" : percentage + "%"}
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
`;
|
`;
|
||||||
@@ -127,7 +126,7 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress }) => {
|
|||||||
|
|
||||||
const percent = ((longestLang.size / totalSize) * 100).toFixed(2);
|
const percent = ((longestLang.size / totalSize) * 100).toFixed(2);
|
||||||
const minGap = 150;
|
const minGap = 150;
|
||||||
const maxGap = 20 + measureText(`${longestLang.name} ${longestLang.text || `${percent}%`}`, 11);
|
const maxGap = 20 + measureText(`${longestLang.name} ${percent}%`, 11);
|
||||||
return flexLayout({
|
return flexLayout({
|
||||||
items: layouts,
|
items: layouts,
|
||||||
gap: maxGap < minGap ? minGap : maxGap,
|
gap: maxGap < minGap ? minGap : maxGap,
|
||||||
@@ -150,7 +149,6 @@ const renderNormalLayout = (langs, width, totalLanguageSize) => {
|
|||||||
name: lang.name,
|
name: lang.name,
|
||||||
color: lang.color || DEFAULT_LANG_COLOR,
|
color: lang.color || DEFAULT_LANG_COLOR,
|
||||||
progress: ((lang.size / totalLanguageSize) * 100).toFixed(2),
|
progress: ((lang.size / totalLanguageSize) * 100).toFixed(2),
|
||||||
text: lang.text,
|
|
||||||
index,
|
index,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -280,12 +278,6 @@ const useLanguages = (topLangs, hide, langs_count) => {
|
|||||||
* @returns {string} Language card SVG object.
|
* @returns {string} Language card SVG object.
|
||||||
*/
|
*/
|
||||||
const renderTopLanguages = (topLangs, options = {}) => {
|
const renderTopLanguages = (topLangs, options = {}) => {
|
||||||
const locale = options.locale
|
|
||||||
const i18n = new I18n({
|
|
||||||
locale,
|
|
||||||
translations: langCardLocales,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
hide_title = false,
|
hide_title = false,
|
||||||
hide_border,
|
hide_border,
|
||||||
@@ -298,13 +290,18 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
|||||||
theme,
|
theme,
|
||||||
layout,
|
layout,
|
||||||
custom_title,
|
custom_title,
|
||||||
|
locale,
|
||||||
langs_count = DEFAULT_LANGS_COUNT,
|
langs_count = DEFAULT_LANGS_COUNT,
|
||||||
border_radius,
|
border_radius,
|
||||||
border_color,
|
border_color,
|
||||||
title = i18n.t("langcard.title"),
|
|
||||||
disable_animations,
|
disable_animations,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
const i18n = new I18n({
|
||||||
|
locale,
|
||||||
|
translations: langCardLocales,
|
||||||
|
});
|
||||||
|
|
||||||
const { langs, totalLanguageSize } = useLanguages(
|
const { langs, totalLanguageSize } = useLanguages(
|
||||||
topLangs,
|
topLangs,
|
||||||
hide,
|
hide,
|
||||||
@@ -345,7 +342,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
|||||||
|
|
||||||
const card = new Card({
|
const card = new Card({
|
||||||
customTitle: custom_title,
|
customTitle: custom_title,
|
||||||
defaultTitle: title,
|
defaultTitle: i18n.t("langcard.title"),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
border_radius,
|
border_radius,
|
||||||
|
|||||||
Reference in New Issue
Block a user