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)
|
||||
- [Language Card Exclusive Options](#language-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)
|
||||
|
||||
# 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>
|
||||
```
|
||||
|
||||
## 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)
|
||||
|
||||
@@ -546,6 +550,23 @@ Since the GitHub API only allows 5k requests per hour, my `https://github-readme
|
||||
|
||||
</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
|
||||
|
||||
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.
|
||||
*/
|
||||
const renderRepoCard = (repo, options = {}) => {
|
||||
const {
|
||||
name,
|
||||
nameWithOwner,
|
||||
description,
|
||||
primaryLanguage,
|
||||
isArchived,
|
||||
isTemplate,
|
||||
starCount,
|
||||
forkCount,
|
||||
} = repo;
|
||||
const {
|
||||
hide_border = false,
|
||||
title_color,
|
||||
@@ -99,42 +109,6 @@ const renderRepoCard = (repo, options = {}) => {
|
||||
border_color,
|
||||
locale,
|
||||
} = 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 header = show_owner ? nameWithOwner : name;
|
||||
@@ -151,25 +125,27 @@ const renderRepoCard = (repo, options = {}) => {
|
||||
const height =
|
||||
(descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight;
|
||||
|
||||
const i18n = new I18n({
|
||||
locale,
|
||||
translations: repoCardLocales,
|
||||
});
|
||||
|
||||
// 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
|
||||
? createLanguageNode(langName, langColor)
|
||||
: "";
|
||||
|
||||
let totalStars
|
||||
let totalForks
|
||||
if (isNaN(starCount)) {
|
||||
totalStars = starCount;
|
||||
} else {
|
||||
totalStars = kFormatter(starCount);
|
||||
}
|
||||
if (isNaN(forkCount)) {
|
||||
totalForks = forkCount;
|
||||
} else {
|
||||
totalForks = kFormatter(forkCount);
|
||||
}
|
||||
|
||||
|
||||
const totalStars = kFormatter(starCount);
|
||||
const totalForks = kFormatter(forkCount);
|
||||
const svgStars = iconWithLabel(icons.star, totalStars, "stargazers");
|
||||
const svgForks = iconWithLabel(icons.fork, totalForks, "forkcount");
|
||||
|
||||
@@ -204,7 +180,15 @@ const renderRepoCard = (repo, options = {}) => {
|
||||
`);
|
||||
|
||||
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">
|
||||
${descriptionSvg}
|
||||
|
||||
+32
-44
@@ -40,12 +40,7 @@ const createTextNode = ({
|
||||
shiftValuePos,
|
||||
bold,
|
||||
}) => {
|
||||
let kValue
|
||||
if (isNaN(value)) {
|
||||
kValue = value;
|
||||
} else {
|
||||
kValue = kFormatter(value);
|
||||
}
|
||||
const kValue = kFormatter(value);
|
||||
const staggerDelay = (index + 3) * 150;
|
||||
|
||||
const labelOffset = showIcons ? `x="25"` : "";
|
||||
@@ -80,6 +75,15 @@ const createTextNode = ({
|
||||
* @returns {string} The stats card SVG object.
|
||||
*/
|
||||
const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
const {
|
||||
name,
|
||||
totalStars,
|
||||
totalCommits,
|
||||
totalIssues,
|
||||
totalPRs,
|
||||
contributedTo,
|
||||
rank,
|
||||
} = stats;
|
||||
const {
|
||||
hide = [],
|
||||
show_icons = false,
|
||||
@@ -102,37 +106,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
locale,
|
||||
disable_animations = false,
|
||||
} = 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);
|
||||
|
||||
@@ -148,35 +121,45 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
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
|
||||
const STATS = {
|
||||
stars: {
|
||||
icon: icons.star,
|
||||
label: starsTitle,
|
||||
label: i18n.t("statcard.totalstars"),
|
||||
value: totalStars,
|
||||
id: "stars",
|
||||
},
|
||||
commits: {
|
||||
icon: icons.commits,
|
||||
label: commitsTitle,
|
||||
label: `${i18n.t("statcard.commits")}${
|
||||
include_all_commits ? "" : ` (${new Date().getFullYear()})`
|
||||
}`,
|
||||
value: totalCommits,
|
||||
id: "commits",
|
||||
},
|
||||
prs: {
|
||||
icon: icons.prs,
|
||||
label: PRsTitle,
|
||||
label: i18n.t("statcard.prs"),
|
||||
value: totalPRs,
|
||||
id: "prs",
|
||||
},
|
||||
issues: {
|
||||
icon: icons.issues,
|
||||
label: issuesTitle,
|
||||
label: i18n.t("statcard.issues"),
|
||||
value: totalIssues,
|
||||
id: "issues",
|
||||
},
|
||||
contribs: {
|
||||
icon: icons.contribs,
|
||||
label: contribsTitle,
|
||||
label: i18n.t("statcard.contribs") + " (last year)",
|
||||
value: contributedTo,
|
||||
id: "contribs",
|
||||
},
|
||||
@@ -232,7 +215,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
});
|
||||
|
||||
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({
|
||||
customTitle: custom_title,
|
||||
defaultTitle: title,
|
||||
defaultTitle: i18n.t("statcard.title"),
|
||||
width,
|
||||
height,
|
||||
border_radius,
|
||||
@@ -323,6 +306,11 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
const labels = Object.keys(STATS)
|
||||
.filter((key) => !hide.includes(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}`;
|
||||
})
|
||||
.join(", ");
|
||||
|
||||
@@ -47,7 +47,6 @@ const getLongestLang = (arr) =>
|
||||
* @param {number} props.index Index of the programming language.
|
||||
* @returns {string} Programming language SVG node.
|
||||
*/
|
||||
|
||||
const createProgressTextNode = ({ width, color, name, progress, index }) => {
|
||||
const staggerDelay = (index + 3) * 150;
|
||||
const paddingRight = 95;
|
||||
@@ -57,7 +56,7 @@ const createProgressTextNode = ({ width, color, name, progress, index }) => {
|
||||
return `
|
||||
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
||||
<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({
|
||||
x: 0,
|
||||
y: 25,
|
||||
@@ -90,7 +89,7 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, index }) => {
|
||||
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
|
||||
<circle cx="5" cy="6" r="5" fill="${color}" />
|
||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
||||
${lang.name} ${hideProgress ? "" : (lang.text || `${percentage}%`)}
|
||||
${lang.name} ${hideProgress ? "" : percentage + "%"}
|
||||
</text>
|
||||
</g>
|
||||
`;
|
||||
@@ -127,7 +126,7 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress }) => {
|
||||
|
||||
const percent = ((longestLang.size / totalSize) * 100).toFixed(2);
|
||||
const minGap = 150;
|
||||
const maxGap = 20 + measureText(`${longestLang.name} ${longestLang.text || `${percent}%`}`, 11);
|
||||
const maxGap = 20 + measureText(`${longestLang.name} ${percent}%`, 11);
|
||||
return flexLayout({
|
||||
items: layouts,
|
||||
gap: maxGap < minGap ? minGap : maxGap,
|
||||
@@ -150,7 +149,6 @@ const renderNormalLayout = (langs, width, totalLanguageSize) => {
|
||||
name: lang.name,
|
||||
color: lang.color || DEFAULT_LANG_COLOR,
|
||||
progress: ((lang.size / totalLanguageSize) * 100).toFixed(2),
|
||||
text: lang.text,
|
||||
index,
|
||||
});
|
||||
}),
|
||||
@@ -280,12 +278,6 @@ const useLanguages = (topLangs, hide, langs_count) => {
|
||||
* @returns {string} Language card SVG object.
|
||||
*/
|
||||
const renderTopLanguages = (topLangs, options = {}) => {
|
||||
const locale = options.locale
|
||||
const i18n = new I18n({
|
||||
locale,
|
||||
translations: langCardLocales,
|
||||
});
|
||||
|
||||
const {
|
||||
hide_title = false,
|
||||
hide_border,
|
||||
@@ -298,13 +290,18 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
theme,
|
||||
layout,
|
||||
custom_title,
|
||||
locale,
|
||||
langs_count = DEFAULT_LANGS_COUNT,
|
||||
border_radius,
|
||||
border_color,
|
||||
title = i18n.t("langcard.title"),
|
||||
disable_animations,
|
||||
} = options;
|
||||
|
||||
const i18n = new I18n({
|
||||
locale,
|
||||
translations: langCardLocales,
|
||||
});
|
||||
|
||||
const { langs, totalLanguageSize } = useLanguages(
|
||||
topLangs,
|
||||
hide,
|
||||
@@ -345,7 +342,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
|
||||
const card = new Card({
|
||||
customTitle: custom_title,
|
||||
defaultTitle: title,
|
||||
defaultTitle: i18n.t("langcard.title"),
|
||||
width,
|
||||
height,
|
||||
border_radius,
|
||||
|
||||
Reference in New Issue
Block a user