Compare commits

...
184 Commits
Author SHA1 Message Date
anuraghazra d8d724c664 chore: added MrAhmadAwais' support badges
Test / build (push) Canceled after 0s
2020-07-29 12:51:55 +05:30
Anurag Hazra df447eee55 Merge pull request #245 from zizi4n5/master
docs: fix typos & improved consistency
2020-07-29 12:26:21 +05:30
Anurag Hazra f38224a8b6 Merge pull request #252 from ngtrhieu/master
docs: add documentation note for uri-encoded names
2020-07-29 12:24:00 +05:30
ngtrhieu 027655f023 docs: add special note for uri-encoded names
add a reminding section for folks who trying to hide c++ or languages with uri-reserved charaters.
2020-07-29 14:43:37 +08:00
zizi4n5 9dde51951b docs: fix typo 2020-07-29 08:58:42 +09:00
anuraghazra 8854c6d6cb chore: added code comments 2020-07-28 23:46:48 +05:30
Anurag Hazra f741ec0073 Merge pull request #240 from anuraghazra/fix-lang-stats
feat: improved top lang fetching & changed card title
2020-07-28 23:42:47 +05:30
anuraghazra 826f0201aa feat: improved top lang fetching & changed title
Test / build (push) Canceled after 0s
2020-07-28 23:33:43 +05:30
457848225b docs(translation): Add german translation of Readme (#171)
* Add german translation of readmes header, table of content and 1st section

* link german readme in default (english) readme

* translate extra pins section to german

* add german translation for extra pins

* fix links in german translation

* add german translation of readmes support section

* minor fixes in german translation of readme after proofreading

* Update readme_de.md

Co-authored-by: Mario Lubenka <mario.lubenka@googlemail.com>

* Update readme_de.md

Co-authored-by: Mario Lubenka <mario.lubenka@googlemail.com>

* Update readme_de.md

Co-authored-by: Mario Lubenka <mario.lubenka@googlemail.com>

Co-authored-by: Anurag Hazra <hazru.anurag@gmail.com>
Co-authored-by: Mario Lubenka <mario.lubenka@googlemail.com>
2020-07-28 21:01:52 +05:30
awrznc 1f22b6938c docs(translation): add japanese translation (#236) 2020-07-28 20:58:19 +05:30
Anurag Hazra f7f8ee7ba0 docs(theme): Auto update theme readme (#232)
Co-authored-by: Github Readme Stats Bot <hazru.anurag@gmail.com>
2020-07-28 17:17:30 +05:30
Paolo TormonandAnurag Hazra 3206c68f71 style(theme): Added Great Gatsby Theme (#231)
* Added Gatsby Theme

* chore: change theme name

Co-authored-by: Anurag Hazra <hazru.anurag@gmail.com>
2020-07-28 17:13:35 +05:30
Sprite 12ec9fd715 docs: fix readme typo (#228) 2020-07-28 17:09:36 +05:30
Jasper Pilgrim 596897f9af docs: Fix typo (#222) 2020-07-28 12:30:28 +05:30
natee 2fa229ac7e docs(translation): Update Chinese readme (#225)
* update Chinese readme

* readme demo usage error
2020-07-28 12:29:37 +05:30
Anurag Hazra 51ba2d206a Merge pull request #180 from moonantonio/master
docs(translation): Update readme_es.md
2020-07-27 22:41:19 +05:30
anuraghazra f82a89fbc0 docs: update themes doc 2020-07-27 22:30:28 +05:30
Anurag Hazra b4882408c6 Merge pull request #219 from anuraghazra/auto-theme-readme
fix: readme generation script
2020-07-27 22:24:53 +05:30
anuraghazra 5076d88a34 fix: readme generation script
Test / build (push) Canceled after 0s
2020-07-27 22:22:38 +05:30
Anurag Hazra 243dd0f204 Merge pull request #214 from judicaelandria/patch-1
style(theme): added algolia theme
2020-07-27 19:24:27 +05:30
Judicael 45cb505a5b added: theme algolia 2020-07-27 16:44:13 +03:00
Leo Hanischandanuraghazra 11757dba42 feat: multiline text in repo description (#206)
* Enable multi line repo card descriptions

* Adjust unit tests to multi line descriptions

* refactor: refactored code & fixed tests

* style: minor height adjustments

* chore: codecov stuck

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-27 14:09:35 +05:30
Anurag Hazra 68198d8b16 Merge pull request #204 from SSARCandy/master
fix: fetch top lang in each repository
2020-07-26 22:19:26 +05:30
ssarcandy d929e8b20a fix: fetch top lang in ech repository 2020-07-27 00:26:50 +08:00
Sagarandanuraghazra b8330a88e1 feat: Added compact layout for top languages card (#179)
* compact layout for top langs card

* 🐞 FIX: most used lang should be first, apply correct colors, removed nested svgs and height set

* 🐞 FIX: conditionally rendering layout compact

* 🐞 FIX: border radius on lang progressbar

* refactor: refactored code & fixed bugs

* fix: toFixed

* chore: change string interpolation

* docs: updated readme

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-26 21:45:23 +05:30
taichi uchihara 3bdcf3d61f docs: fix typo (#202) 2020-07-26 20:44:09 +05:30
anuraghazra 845e90b42e Merge branch 'master' of https://github.com/anuraghazra/github-readme-stats 2020-07-26 14:46:58 +05:30
anuraghazra 59e295f35b chore: my godh please work 2020-07-26 14:46:23 +05:30
Anurag Hazra 8be88461b4 chore: change color codes to lowercase (#198)
* chore: change color codes to lowercase

* fix: tests
2020-07-26 14:44:20 +05:30
Anurag Hazra 9bd1bad491 Merge pull request #197 from anuraghazra/auto-theme-readme
fix: workflow file in readme generator
2020-07-26 14:33:27 +05:30
anuraghazra 1f79d503bd fix: workflow file 2020-07-26 14:32:17 +05:30
Anurag Hazra 1b23175fa6 Merge pull request #195 from the-it-weirdo/patch-1
style(theme): adds a new theme: "blue-green"
2020-07-26 14:30:33 +05:30
Anurag Hazra 9e8d4aeb38 Merge pull request #196 from anuraghazra/auto-theme-readme
infra: added auto theme readme generator script
2020-07-26 14:27:06 +05:30
anuraghazra ef57cc7dcf fix: readme generate script 'Add new theme' table item 2020-07-26 14:24:13 +05:30
anuraghazra f0a0dfe51c infra: added auto theme readme generator script 2020-07-26 14:11:30 +05:30
Debaleen Das Spandan c1438fe7c5 adds a new theme: "blue-green"
I added a new theme. Hope you like it.
2020-07-26 13:50:57 +05:30
anuraghazra 7f220bb072 docs: updated & fixed themes.md 2020-07-26 13:15:11 +05:30
Ming Tsai 57cf490e3e style(themes): add buefy theme (#193)
* styles(themes): add buefy configuration

* docs(theme):  update table

* docs: add missing parentheses on the example
2020-07-26 13:10:35 +05:30
Ahmad Awais ⚡️ 6d1b1e220c 👌 IMPROVE: Shades of Purple Icon color (#190)
Looks like this https://github-readme-stats.vercel.app/api?username=maedahbatool&show_icons=true&theme=shades-of-purple&icon_color=B362FF
2020-07-25 20:56:23 +05:30
Anurag Hazra 662ae95b02 docs: updated themes table 2020-07-25 20:54:46 +05:30
Anurag HazraandVasu Gamdha e38df2932c docs: added CONTRIBUTING.md (#188)
* Create CONTRIBUTING.md

* docs: added contributing.md

Co-authored-by: Vasu Gamdha <40864108+vasugamdha@users.noreply.github.com>
2020-07-25 20:34:42 +05:30
Yash 64d1fb46c3 style(theme): Add Night-Owl Theme (#186)
* Added Night-Owl Theme

* corrected quotes

* minor changes
2020-07-25 19:44:42 +05:30
SagarandAnurag Hazra 753f0b9fec feat(themes): Added shades of purple theme (#185)
* 📦 NEW: shade of purple theme

* style(theme): icons color update

* chore: change theme name

Co-authored-by: Anurag Hazra <hazru.anurag@gmail.com>
2020-07-25 18:26:15 +05:30
Anurag Hazra f50938c908 Merge pull request #182 from anuraghazra/show-statcard-org-error
chore: added secondary warning message in renderError
2020-07-25 13:50:18 +05:30
anuraghazra 6d5a828542 chore: added secondary warning message in renderError 2020-07-25 13:29:32 +05:30
MoonAntonio 463564382a Update readme_es.md 2020-07-24 20:52:13 +02:00
Anurag Hazra e32ab3f973 Merge pull request #178 from anuraghazra/fix-unicode
fix: weird unicode char
2020-07-24 23:46:38 +05:30
anuraghazra 2812ce0fd3 fix: weird unicode char 2020-07-24 23:35:20 +05:30
Feng L.HandAnurag Hazra f9b14c9442 style(theme): Added Vue theme (#143)
Add Vue style.

Co-authored-by: Anurag Hazra <hazru.anurag@gmail.com>
2020-07-24 22:06:19 +05:30
Naufal Yudhistira 7b509d3128 style(theme): Add monokai theme (#124)
* style(theme): Add new theme

* style(theme): Change monokai style

* style(theme): Change monokai style

* feat(theme): Add theme randomizer

* fix(theme): Make randomizer asynchronous

* feat(theme): Add theme randomizer

* fix(theme): Fix everything back ._.

* fix(theme): Change theme based on review
2020-07-24 21:58:23 +05:30
leavesster f355f48bb4 doc(translation): add Hide individual languages in readme_cn (#173) 2020-07-24 20:09:35 +05:30
Fábio Rosadoandanuraghazra 4c0518616f feat: Add count_private flag to count private contributions (#148)
* Add private contributions count

* Remove unused var and add tests

* Update readme

* fix: tests & minor code formating

* docs: updated docs

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-24 19:34:38 +05:30
Mario Lubenkaandanuraghazra 3a9ded0a09 feat(repocard): parse emojis in description (#67)
* feat: add remove_emojis option

Removes emojis in repo card description

* feat: parse emojis

* feat: rename remove_emojis to show_emojis option for consistency

* docs: update docs

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-24 18:56:26 +05:30
obsidian 7973805cf7 doc(translation): fixed typo and retranslated some titles. (#172) 2020-07-24 18:23:50 +05:30
Joydip Roy a7d56c3a04 docs: changed bugs and feature link (#137)
* docs: changed bugs and feature link

* link change as per suggestion
2020-07-24 18:15:20 +05:30
obsidian 13d9da68ff docs: Added content about "Top Langs" in readme_cn.md (#144)
* doc: supplement readme_cn.md.

* rm -rf .idea
2020-07-24 17:46:45 +05:30
Liamandanuraghazra 9624cf6acd fix: stats hide_border bug (#151)
* fix: stats hide border bug

* fix white border on dark mode issue

* chore: refactor code & tests

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-24 13:07:09 +05:30
Anurag Hazra 7a8510641c Merge pull request #166 from terror/typo-fix-top-lang
docs: fix typo in top lang section
2020-07-24 12:47:41 +05:30
Liam bd13399e7f docs: fix typo in top lang section 2020-07-23 23:25:25 -04:00
Anurag Hazra aa44bd7615 fix: hide parameter array implementation (#162)
* fix: hide paramter array implementation

* chore: parseArray str check

* docs: updated readme

* chore: add codecov threshold

* docs: update docs
2020-07-23 21:01:23 +05:30
Arjun Mahishiandanuraghazra c2adcfd6fe feat: hide specific languages in "Top languages" card (#150)
* add new query param to hide specific languages in top languages card

* [top-langs] add function to clean out the provided lang name

* [top-langs] rename 'hide_lang' => 'hide', refactor logic for parsing the list of provided languages to hide

* [top-langs] take list of languages to hide, as a json array

* chore: minor changes

* docs: added docs for hide lang

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-23 19:47:04 +05:30
Anurag Hazra 17c33cd322 chore: added buymeacoffee link 2020-07-23 14:18:50 +05:30
Anurag Hazra 1c4cb84916 Merge pull request #156 from anuraghazra/fix-repo-lang-fallback
fix: languages color fallback & hide lang if not present
2020-07-23 13:52:17 +05:30
anuraghazra d394add560 fix: languages color fallback & hide lang if not present 2020-07-23 13:41:37 +05:30
Joydip Royandanuraghazra 5ed75e11be feat: added isTemplate badge & refactored console.logs (#146)
* feat: template option added husky added for same commit disable console in test \
logger utils added env checked for log modified git ignore

* changed are done as per the suggesstion

* changed style and font

* text color dynamic

* fix border and using .bagde class as common

* simplified the badge svg code through a common method

* chore: updated css & fixed tests

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-23 13:05:50 +05:30
Anurag Hazra 03f55e809e infra: added Codecov yml (#149)
* chore: test codecov

* fix: workflow file

* fix: workflow file

* chore: added coverage in vercelignore

* docs: added codecov badge

* chore: add codecov.yml
2020-07-22 23:46:10 +05:30
Anurag Hazra 63ba6e6601 infra: Added codecov test coverage (#147)
* chore: test codecov

* fix: workflow file

* fix: workflow file

* chore: added coverage in vercelignore

* docs: added codecov badge
2020-07-22 23:26:10 +05:30
Anurag Hazra dc3e9a59f2 Merge pull request #133 from anuraghazra/add-redirect
chore: add redirect to gh repo
2020-07-21 21:26:46 +05:30
anuraghazra 8284be105d chore: add redirect to gh repo 2020-07-21 21:24:39 +05:30
Anurag Hazra 6c16020c53 Merge pull request #131 from anuraghazra/fix-sort-langcard
fix: fixed sorting of languages by percentage
2020-07-21 20:00:56 +05:30
anuraghazra 8a8ff5b390 fix: fixed sorting of languages by percentage 2020-07-21 19:58:13 +05:30
Anurag Hazra 4b01b4187c Merge pull request #129 from nombrekeff/patch-2
docs: Added "prussian" theme to themes/README preview
2020-07-21 19:53:14 +05:30
Manolo Edge c3de4675c5 Added "prussian" theme to themes/README preview 2020-07-21 16:10:44 +02:00
Anurag Hazra ad87824f3c Merge pull request #128 from anuraghazra/top-langs
feat: added Top languages card
2020-07-21 18:59:02 +05:30
anuraghazra bc7a9857a9 docs: added top lang card docs
Test / build (push) Canceled after 0s
2020-07-21 18:53:24 +05:30
anuraghazra 4a8fd03d8d feat: added hide_langs_below option 2020-07-21 18:37:16 +05:30
anuraghazra 7c104cf8c5 tests: added tests for renderTopLanguages & top-langs 2020-07-21 18:04:58 +05:30
anuraghazra b50555ad9a tests: added test for fetchTopLanguages 2020-07-21 17:22:22 +05:30
anuraghazra 411d8f1ffa feat: added Top languages card 2020-07-21 17:03:05 +05:30
Liam 938df3f7ea docs: update vercel deploy screenshot guide (#122) 2020-07-21 13:17:38 +05:30
Anurag Hazra 92dcc89a3d tests: added more tests for themes (#125) 2020-07-21 13:15:53 +05:30
Divya Kelaskar 815916b3f1 chore(themes): Added Prussian theme by @divyakelaskar (#123)
* Update .gitignore

* added new theme

* reverted changes in .gitignore
2020-07-21 13:04:43 +05:30
Manolo Edge d04d682eb2 docs: edited readme_es, changed "customizar" with "personalizar" (#118) 2020-07-20 23:55:23 +05:30
José De Freitas f78136c533 docs(translation): Spanish translation file (#116)
* Create readme_es.md

* Fix typos

* Add link to Spanish translation
2020-07-20 22:09:39 +05:30
anuraghazra f4f277a8e2 docs: added cache_seconds docs & updated themes readme cache to 24hour 2020-07-20 22:02:50 +05:30
Anurag Hazra a5fa1a0d1b Merge pull request #117 from anuraghazra/custom-cache
feat: added ability to set custom cache
2020-07-20 21:56:16 +05:30
anuraghazra fdf445d734 feat: added ability to set custom cache
Test / build (push) Canceled after 0s
2020-07-20 21:43:51 +05:30
Manolo Edge 9fb28c9112 docs: added themes preview (#115)
* Adding themes.md file

* Update and rename THEMES.md to README.md

* Added link to theme preview README

* Removed Dark/Light sections, combined into 1 table

* Update README.md

* Updated "Add your theme" link to open in edit mode
2020-07-20 21:10:23 +05:30
TonyTang 8fc85c68b1 docs(translation): Update & Improvements on Existing Simplified Chinese Translation of README (#109)
* Update Simplified Chinese Version of README

* Fix a few characters for a more precise translation.

* Fix a character for a more precise translation.
2020-07-20 12:16:04 +05:30
Sreetam Dasandanuraghazra dd2c7ed278 fix: proper apostrophe in stats card's header
* fix: remove 's' after apostrophe if name ends with 's'

* fix: respect GITHUB_TOKEN env var before PAT_s

* revert: verbose console log for retries

* Revert "fix: respect GITHUB_TOKEN env var before PAT_s"

This reverts commit 2becbe595c2df4580aa81d690482187b1e6ba512.

* test: added test for title apostrophe

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
2020-07-20 00:02:07 +05:30
Anurag Hazra 9a7ed8f192 docs: added themes screenshot 2020-07-19 21:35:57 +05:30
Anurag Hazra 71c63abe3a Merge pull request #106 from hi120ki/fix-style-collapses-on-stats-card
fix: text collapses on stats card with proper macos font fallback
2020-07-19 21:25:13 +05:30
Hi120ki 30da0119bf fix style collapses on stats card by css font config 2020-07-20 00:24:54 +09:00
Anurag Hazra 2c26329e13 feat: added inbuilt themes (#105)
* feat: added inbuilt themes

* docs: added theming docs

* docs: update docs
2020-07-19 20:34:41 +05:30
Anurag Hazra b4a9bd4468 Merge pull request #102 from JalenChuh/master
docs(translation): add Chinese translation
2020-07-19 18:08:58 +05:30
Anurag Hazra f5cd85f11f Merge pull request #103 from colmugx/patch-1
docs: fix readme hide options
2020-07-19 14:16:06 +05:30
ColMugX c436ed1121 docs: README.md 2020-07-19 14:33:48 +08:00
Jalen Chuh f6c901b938 📝 add Chinese translation 2020-07-19 12:00:32 +08:00
Anurag Hazra e9dc35318b Merge pull request #100 from anuraghazra/refactor-flex
refactor: added FlexLayout for flex layouts
2020-07-18 23:14:47 +05:30
anuraghazra 253eb39b19 refactor: added FlexLayout for flex layouts
Test / build (push) Canceled after 0s
2020-07-18 22:44:27 +05:30
Omid Nikrah 06f0021660 chore: Hide star and fork stats in repo card if count of equals to zero (#97)
* add show_icons option to show/hide star and fork icon in repo cards

* resolve conflict

* resolve conflict

* hide star and fork stats in repo card if count of equals to zero

* HOTFIX

* HOTFIX
2020-07-18 22:20:04 +05:30
Anurag Hazra f27daeb931 Merge pull request #99 from anuraghazra/feat/archive-badge
feat: show archive badge if repo is archived
2020-07-18 20:33:19 +05:30
anuraghazra b6aa462881 design: make badge look good on different colors 2020-07-18 20:28:01 +05:30
anuraghazra be79fe4051 feat: show archive badge if repo is archive 2020-07-18 20:20:36 +05:30
Anurag Hazra b039fa16af Merge pull request #86 from edisonlee55/master
fix: fixed CSS scale() on WebKit causing rendering issues

So I have used the solution provided here (Reference):
CSS transform on SVG text element not working in Safari - Stack Overflow - https://stackoverflow.com/a/61398694/4333875

They have encountered issue with `transform: translate()` and filed a bug report, but `transform: scale()` is also having issue on our side.

Also, the minimum reproducible example is [here](https://stackoverflow.com/questions/62964270/css-scale-animation-in-svg-text-tag-not-working-on-safari) on Stack Overflow.
(the word `Test` won't show up **on Safari**)
2020-07-18 18:06:57 +05:30
Edison Lee 61442cb359 fix: fixed CSS scale() issue on WebKit by wrap up text in g tag 2020-07-18 10:37:34 +08:00
anuraghazra 431f2a0b5b docs: update options table 2020-07-17 23:03:46 +05:30
Anurag Hazra 2709d5b434 feat: added hide_title option (#88) 2020-07-17 23:01:16 +05:30
Anurag Hazra 96f89ad2b7 Merge pull request #81 from reed-jones/patch-1
docs: improved customization table
2020-07-17 20:54:48 +05:30
Reed Jones 7dcb60f576 update default bg_color 2020-07-17 09:16:29 -06:00
Anurag Hazra d5f410d4ec Merge pull request #84 from anuraghazra/fix-private-repos
fix: filter out private repos
2020-07-17 12:46:33 +05:30
anuraghazra 1c0332352a fix: filter out private repos
filtering out private repos just in case, otherwise if a user somehow
found out the name of any of the PAT owner's private -
(considering they created the PAT with private repo access) repos's name
then they could see the repo in github extra pins.
2020-07-17 12:39:19 +05:30
Reed Jones 4febc6644a Changed hex colors to be copy/paste workable
I was a little confused at first setting the bg_color, since it said `hex color` but had `rgba(...)` so I copied the title color which of course didn't work since it started with `#` I think this makes it a bit clearer how to use it!
2020-07-16 17:08:43 -06:00
Anurag Hazra 6fedd69f86 docs: added support the project section 2020-07-16 23:27:23 +05:30
Anurag Hazra 1078c85455 docs: added github-readme-stats logo! 2020-07-16 19:35:18 +05:30
omrilotan 9e4d83247e feat: display username in repo card (optional) (#78)
* Display username in repo card (optional)

* fix: show_owner boolean flag & used nameWithOwner from gql api
2020-07-16 18:03:33 +05:30
anuraghazra 597dac2985 Merge branch 'master' of https://github.com/anuraghazra/github-readme-stats 2020-07-16 17:22:59 +05:30
anuraghazra 0ea2668176 docs: updated vercel deploy docs 2020-07-16 17:22:02 +05:30
Anurag Hazra 1091fc7695 chore: Update issue templates 2020-07-16 16:18:44 +05:30
6d4fbfecfb perf: align icons and text vertical (#33)
* improve: improved rating algorithm wip

* Fixed typos, punctuation [...]

Corrected many instances of "Github" to "GitHub", fixed the punctuation on some sections, fixed the ~lack of~ uppercase chars in others, tweaked the grammar a bit, and added the Vercel guide to a spoiler-ish section so it's only visible if you click to expand.

<3

* fix: github rate limiter with multiple PATs

* perf: vertically align text left

* refactor: refactored retryer logic & handled invalid tokens

* chore: remove redundant codes

`axios` is Promise based, there is no need to wrap it into a Promise constructor again

* fix: query param booleans

* design: fixed rank alignment

* chore: rebase from master

* fix: fixed repo card breaking in absence of primaryLanguage

* fix: fixed stars count #39 & fixed progressbar percentage

* perf: replace emoji icons with GitHub SVG icons

* chore: added funding link

* refactor: refacted icons to another file

* test: added test for icons

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
Co-authored-by: Micael Jarniac <micael@jarniac.com>
Co-authored-by: JounQin <admin@1stg.me>
2020-07-16 15:51:21 +05:30
Anurag Hazra 331295139d Merge pull request #77 from anuraghazra/funding
chore: added funding link
2020-07-16 14:06:20 +05:30
anuraghazra a726e8dbaf chore: added funding link
Test / build (push) Canceled after 0s
2020-07-16 14:02:15 +05:30
Anurag Hazra 817f736b4e Merge pull request #61 from JounQin/patch-1
chore: remove redundant codes
2020-07-15 20:37:42 +05:30
Anurag Hazra 987fb0c30d Merge pull request #49 from anuraghazra/ranking-algo
improve: improved rating algorithm wip

fix: fixed #39
2020-07-15 20:10:53 +05:30
anuraghazra 96d37c706f fix: fixed stars count #39 & fixed progressbar percentage
Test / build (push) Canceled after 0s
2020-07-15 20:00:22 +05:30
anuraghazra 3a24ad62b1 Merge branch 'master' into ranking-algo 2020-07-15 19:47:07 +05:30
Anurag Hazra 989079e4fd Merge pull request #64 from anuraghazra/fix-nolang
fix: fixed repo card breaking in absence of primaryLanguage
2020-07-15 19:39:40 +05:30
anuraghazra 20bddc3a0c fix: fixed repo card breaking in absence of primaryLanguage
Test / build (push) Canceled after 0s
2020-07-15 19:37:04 +05:30
JounQin 95f1233ab9 chore: rebase from master 2020-07-15 21:55:04 +08:00
JounQin 1a53fb9a84 Merge branch 'master' into patch-1 2020-07-15 21:41:48 +08:00
Anurag Hazra 19bacf780a Merge pull request #63 from anuraghazra/fix-rank-alignment
design: fixed rank alignment
2020-07-15 19:01:39 +05:30
anuraghazra 89bb8fea42 design: fixed rank alignment 2020-07-15 18:59:38 +05:30
Anurag Hazra 0bade9be77 Merge pull request #62 from anuraghazra/fix-queryparams
fix: query param booleans
2020-07-15 18:34:19 +05:30
anuraghazra 5fce46d67c fix: query param booleans
Test / build (push) Canceled after 0s
2020-07-15 18:32:16 +05:30
Anurag Hazra 00ce9d37d2 Merge pull request #54 from MicaelJarniac/patch-1
docs: Fixed typos, punctuation
2020-07-15 18:16:27 +05:30
Anurag Hazra 9328a57ea6 Merge pull request #58 from anuraghazra/fix-ratelimit
fix: increase github rate limit with multiple PATs

- special thanks to @filiptronicek @ApurvShah007 @garvit-joshi for helping out :D
2020-07-15 17:53:43 +05:30
JounQin 579ed2bab8 chore: remove redundant codes
`axios` is Promise based, there is no need to wrap it into a Promise constructor again
2020-07-15 20:22:06 +08:00
anuraghazra 429d65a52b refactor: refactored retryer logic & handled invalid tokens
Test / build (push) Canceled after 0s
2020-07-15 17:46:31 +05:30
anuraghazra 3d8dea90e2 fix: github rate limiter with multiple PATs 2020-07-15 14:59:25 +05:30
Micael Jarniac 47370adbbb Fixed typos, punctuation [...]
Corrected many instances of "Github" to "GitHub", fixed the punctuation on some sections, fixed the ~lack of~ uppercase chars in others, tweaked the grammar a bit, and added the Vercel guide to a spoiler-ish section so it's only visible if you click to expand.

<3
2020-07-14 18:08:52 -03:00
Anurag Hazra 2efb399f33 Merge pull request #53 from filiptronicek/master
docs: Add "Host yourself" docs
2020-07-15 01:19:12 +05:30
Filip Troníček dcde5f8205 Crop the image a bit 2020-07-14 21:40:38 +02:00
Filip Troníček 3b3ce41ae4 Fix img positions 2020-07-14 21:32:06 +02:00
Filip Troníček e3519ceb4c Add documentation for Vercel setup 2020-07-14 21:30:03 +02:00
Anurag Hazra c297798889 Merge pull request #50 from anuraghazra/animations
feat: added animations!
2020-07-14 21:04:11 +05:30
anuraghazra 6059cb1691 feat: added animations!
Test / build (push) Canceled after 0s
2020-07-14 20:04:36 +05:30
anuraghazra 2658a8737d improve: improved rating algorithm wip 2020-07-14 14:08:57 +05:30
Anurag Hazra cbd0e583ff Merge pull request #48 from anuraghazra/fix-circl-rank-rim
design: added a rim to circle rank's progress
2020-07-14 13:12:56 +05:30
anuraghazra 6f79b495a6 design: added a rim to circle rank's progress 2020-07-14 13:02:45 +05:30
Anurag Hazra 014f710e12 fix: firefox layout breaking (#45)
* fix: firefox layout breaking

* refactor: refactored code and added comments

* chore: remove useragent dep
2020-07-13 23:47:04 +05:30
Anurag Hazra 29d3ea63f8 Merge pull request #40 from anuraghazra/ranking
feat: added rankings
2020-07-13 20:04:52 +05:30
anuraghazra c272a21342 docs: added rank docs
Test / build (push) Canceled after 0s
2020-07-13 19:50:43 +05:30
anuraghazra db49ca7b71 feat: added rankings 2020-07-13 19:41:47 +05:30
Anurag Hazra 8e8d7dd64d Merge pull request #37 from anuraghazra/fix-name-null
fix: fallback to login if name not found
2020-07-13 16:15:50 +05:30
anuraghazra d56ef0d091 fix: fallback to login if name not found
Test / build (push) Canceled after 0s
2020-07-13 16:14:13 +05:30
Anurag Hazra fdeaad36f1 Merge pull request #35 from anuraghazra/readme-update
docs: updated readme
2020-07-13 15:01:40 +05:30
anuraghazra e83c21d9f2 docs: updated readme
Test / build (push) Canceled after 0s
2020-07-13 14:56:59 +05:30
Anurag Hazra 61737f6985 Merge pull request #32 from anuraghazra/fix-stars
fix: fixed total stars count stat
2020-07-12 21:54:17 +05:30
anuraghazra 8e0fa7fbec fix: fixed total stars count stat
Test / build (push) Canceled after 0s
2020-07-12 21:51:56 +05:30
Anurag Hazra 46369f5667 Merge pull request #31 from anuraghazra/integration-tests
tests: added integration tests for the api endpoints
2020-07-12 20:11:44 +05:30
anuraghazra 40e0937100 tests: added integration tests for the api endpoints
Test / build (push) Canceled after 0s
2020-07-12 20:08:01 +05:30
Anurag Hazra 3e9ce48777 Merge pull request #29 from omidnikrah/master
refactor: added request helper function to reduce code duplication
2020-07-12 14:37:20 +05:30
Anurag Hazra 5f4ada0afb docs: fix readme typo 2020-07-12 14:18:25 +05:30
Anurag Hazra 4b3e866478 fix: readme invalid markdown 2020-07-12 14:04:02 +05:30
anuraghazra b2aa92c53e fix: tests 2020-07-12 13:57:35 +05:30
anuraghazra 9a87dc02a2 chore: fallback to white color 2020-07-12 13:54:43 +05:30
Omid Nikrah 7a7ad717a8 resolve conflicts 2020-07-12 12:52:40 +04:30
Anurag Hazra 47eb35a7ec Merge pull request #27 from JoaoVSouto/master
feat: added card customization options
2020-07-12 13:24:50 +05:30
anuraghazra 5cf82b0194 docs: added docs for customization options 2020-07-12 13:16:30 +05:30
Omid Nikrah 208076e7f4 update request util 2020-07-12 12:12:06 +04:30
anuraghazra 9af56df2c1 feat: added bg_color options, added RepoCard color options too 2020-07-12 12:46:08 +05:30
JoaoVSouto 61a0f517db test: adds colors checking tests 2020-07-11 23:28:28 -03:00
Omid Nikrah 001062db21 add request utils + remove duplicate codes 2020-07-12 00:45:58 +04:30
JoaoVSouto 59b8bded9c feat: receives text, title and icon colors from URL 2020-07-11 16:01:44 -03:00
anuraghazra c25319ee6c docs: added badges 2020-07-11 23:42:14 +05:30
anuraghazra 73d26e78fd chore: delete package-lock.json 2020-07-11 23:30:31 +05:30
Anurag Hazra 2d691da3aa Merge pull request #26 from anuraghazra/tests
tests: Added tests & refactored files
2020-07-11 23:28:40 +05:30
anuraghazra 4488e9292b chore: added gh action
Test / build (push) Canceled after 0s
2020-07-11 23:25:31 +05:30
anuraghazra 3180144fa4 chore: ignore lock file 2020-07-11 23:20:42 +05:30
Anurag Hazra 61a2866aff Merge pull request #24 from anuraghazra/add-license-1
chore: add LICENSE
2020-07-11 22:14:07 +05:30
Anurag Hazra 7e3278c708 chore: add LICENSE
closes #23
2020-07-11 22:12:49 +05:30
anuraghazra 45941f6c70 test: added test & refactored files 2020-07-11 22:10:13 +05:30
anuraghazra 223bd659ab docs: added deploy button 2020-07-11 14:33:29 +05:30
Anurag Hazra c072662cda Merge pull request #21 from anuraghazra/fix-desc
fix: description trimming & htmlEncode
2020-07-11 14:20:30 +05:30
50 changed files with 5148 additions and 364 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ["https://www.paypal.me/anuraghazra", "https://www.buymeacoffee.com/anuraghazra"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+20
View File
@@ -0,0 +1,20 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots / Live demo link (paste the github-readme-stats link as markdown image)**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+35
View File
@@ -0,0 +1,35 @@
name: Generate Theme Readme
on:
push:
branches:
- master
paths:
- "themes/index.js"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: setup node
uses: actions/setup-node@v1
with:
node-version: "12.x"
- name: npm install, generate readme
run: |
npm install
npm run theme-readme-gen
env:
CI: true
- name: Run Script
uses: skx/github-action-tester@master
with:
script: ./scripts/push-theme-readme.sh
env:
CI: true
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
GH_REPO: ${{ secrets.GH_REPO }}
+39
View File
@@ -0,0 +1,39 @@
name: Test
on:
push:
branches:
- "*"
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: "12.x"
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-npm-cache-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-cache-
- name: Install & Test
run: |
npm install
npm run test
- name: Code Coverage
uses: codecov/codecov-action@v1
+5 -1
View File
@@ -1,3 +1,7 @@
.vercel
.env
node_modules
node_modules
package-lock.json
*.lock
.vscode/
coverage
+3 -1
View File
@@ -1 +1,3 @@
.env
.env
package-lock.json
coverage
+75
View File
@@ -0,0 +1,75 @@
# Contributing to [github-readme-stats](https://github.com/anuraghazra/github-readme-stats)
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
- Reporting a issue
- Discussing the current state of the code
- Submitting a fix
- Proposing new features
- Becoming a maintainer
## All Changes Happen Through Pull Requests
Pull requests are the best way to propose changes. We actively welcome your pull requests:
1. Fork the repo and create your branch from `master`.
1. If you've added code that should be tested, add some tests' example.
1. If you've changed APIs, update the documentation.
1. Issue that pull request!
## Local Development
To run & test github-readme-stats you need to follow few simple steps :-
_(make sure you already have a [vercel](https://vercel.com/) account)_
1. Install [Vercel CLI](https://vercel.com/download)
1. Fork the repository and clone the code to your local machine
1. Run the command "vercel" in the root and follow the steps there
1. Create a `.env` file in the root of the directory
1. In the .env file add a new variable named "PAT" with your [github Personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token)
1. Run the command "vercel dev" to start a development server at https://localhost:3000
## Themes Contribution
GitHub Readme Stats supports custom theming and you can also contribute new themes!
All you need to do is edit [themes/index.js](./themes/index.js) file and add your theme at the end of the file.
While creating the Pull request to add a new theme **don't forget to add a screenshot of how your theme looks**, you can also test how it looks using custom url parameters like `title_color`, `icon_color`, `bg_color`, `text_color`
> NOTE: If you are contributing your theme just because you are using it personally, then you can [customize the looks](./readme.md#customization) of your card with URL params instead.
## Any contributions you make will be under the MIT Software License
In short, when you submit changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
## Report issues/bugs using GitHub's [issues](https://github.com/anuraghazra/github-readme-stats/issues)
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/anuraghazra/github-readme-stats/issues/new/choose); it's that easy!
### Bug Reports
**Great Bug Reports** tend to have:
- A quick summary and/or background
- Steps to reproduce
- Be specific!
- Share the snapshot, if possible.
- GitHub Readme Stats' live link
- What actually happens
- What you expected would happen
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
People _love_ thorough bug reports. I'm not even kidding.
### Feature Request
**Great Feature Requests** tend to have:
- A quick idea summary
- What & why you wanted to add the specific feature
- Additional Context like images, links to resources to implement the feature etc etc.
## License
By contributing, you agree that your contributions will be licensed under its [MIT License](./LICENSE).
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Anurag Hazra
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+52 -141
View File
@@ -1,156 +1,67 @@
const axios = require("axios");
const { renderError, kFormatter } = require("../utils");
require("dotenv").config();
async function fetchStats(username) {
if (!username) throw Error("Invalid username");
const res = await axios({
url: "https://api.github.com/graphql",
method: "post",
headers: {
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
},
data: {
query: `
query userInfo($login: String!) {
user(login: $login) {
name
repositoriesContributedTo(first: 100, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount
}
contributionsCollection {
totalCommitContributions
}
pullRequests(first: 100) {
totalCount
}
issues(first: 100) {
totalCount
}
repositories(first: 100) {
nodes {
stargazers {
totalCount
}
}
}
}
}
`,
variables: {
login: username,
},
},
});
const stats = {
name: "",
totalPRs: 0,
totalCommits: 0,
totalIssues: 0,
totalStars: 0,
contributedTo: 0,
};
if (res.data.errors) {
console.log(res.data.errors);
throw Error("Could not fetch user");
}
const user = res.data.data.user;
stats.name = user.name;
stats.totalIssues = user.issues.totalCount;
stats.totalCommits = user.contributionsCollection.totalCommitContributions;
stats.totalPRs = user.pullRequests.totalCount;
stats.contributedTo = user.repositoriesContributedTo.totalCount;
stats.totalStars = user.repositories.nodes.reduce((prev, curr) => {
return prev + curr.stargazers.totalCount;
}, 0);
return stats;
}
const createTextNode = (icon, label, value, lheight) => {
const classname = icon === "★" && "star-icon";
return `
<tspan x="25" dy="${lheight}" class="stat bold">
<tspan class="icon ${classname}" fill="#4C71F2">${icon}</tspan> ${label}:</tspan>
<tspan x="155" dy="0" class="stat">${kFormatter(value)}</tspan>
`;
};
const renderSVG = (stats, options) => {
const {
name,
totalStars,
totalCommits,
totalIssues,
totalPRs,
contributedTo,
} = stats;
const { hide, show_icons, hide_border, line_height } = options || {};
const lheight = line_height || 25;
const STAT_MAP = {
stars: createTextNode("★", "Total Stars", totalStars, lheight),
commits: createTextNode("🕗", "Total Commits", totalCommits, lheight),
prs: createTextNode("🔀", "Total PRs", totalPRs, lheight),
issues: createTextNode("ⓘ", "Total Issues", totalIssues, lheight),
contribs: createTextNode("📕", "Contributed to", contributedTo, lheight),
};
const statItems = Object.keys(STAT_MAP)
.filter((key) => !hide.includes(key))
.map((key) => STAT_MAP[key]);
const height = 45 + (statItems.length + 1) * lheight;
return `
<svg width="495" height="${height}" viewBox="0 0 495 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
.bold { font-weight: 700 }
.icon {
display: none;
${!!show_icons && "display: block"}
}
</style>
${
!hide_border &&
`<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>`
}
<text x="25" y="35" class="header">${name}'s GitHub Stats</text>
<text y="45">
${statItems}
</text>
</svg>
`;
};
const {
renderError,
parseBoolean,
parseArray,
clampValue,
CONSTANTS,
} = require("../src/utils");
const fetchStats = require("../src/fetchStats");
const renderStatsCard = require("../src/renderStatsCard");
module.exports = async (req, res) => {
const { username, hide, hide_border, show_icons, line_height } = req.query;
const {
username,
hide,
hide_title,
hide_border,
hide_rank,
show_icons,
count_private,
line_height,
title_color,
icon_color,
text_color,
bg_color,
theme,
cache_seconds,
} = req.query;
let stats;
res.setHeader("Content-Type", "image/svg+xml");
try {
stats = await fetchStats(username);
stats = await fetchStats(username, parseBoolean(count_private));
} catch (err) {
return res.send(renderError(err.message));
return res.send(
renderError(
err.message,
"Make sure the provided username is not an organization"
)
);
}
const cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.THIRTY_MINUTES, 10),
CONSTANTS.THIRTY_MINUTES,
CONSTANTS.ONE_DAY
);
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
res.send(
renderSVG(stats, {
hide: JSON.parse(hide || "[]"),
show_icons,
hide_border,
renderStatsCard(stats, {
hide: parseArray(hide),
show_icons: parseBoolean(show_icons),
hide_title: parseBoolean(hide_title),
hide_border: parseBoolean(hide_border),
hide_rank: parseBoolean(hide_rank),
line_height,
title_color,
icon_color,
text_color,
bg_color,
theme,
})
);
};
+53 -118
View File
@@ -1,132 +1,67 @@
const axios = require("axios");
const { renderError, kFormatter, encodeHTML } = require("../utils");
require("dotenv").config();
async function fetchRepo(username, reponame) {
const res = await axios({
url: "https://api.github.com/graphql",
method: "post",
headers: {
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
},
data: {
query: `
fragment RepoInfo on Repository {
name
stargazers {
totalCount
}
description
primaryLanguage {
color
id
name
}
forkCount
}
query getRepo($login: String!, $repo: String!) {
user(login: $login) {
repository(name: $repo) {
...RepoInfo
}
}
organization(login: $login) {
repository(name: $repo) {
...RepoInfo
}
}
}
`,
variables: {
login: username,
repo: reponame,
},
},
});
const data = res.data.data;
if (!data.user && !data.organization) {
throw new Error("Not found");
}
if (data.organization === null && data.user) {
if (!data.user.repository) {
throw new Error("User Repository Not found");
}
return data.user.repository;
}
if (data.user === null && data.organization) {
if (!data.organization.repository) {
throw new Error("Organization Repository Not found");
}
return data.organization.repository;
}
}
const renderRepoCard = (repo) => {
const { name, description, primaryLanguage, stargazers, forkCount } = repo;
const height = 120;
const shiftText = primaryLanguage.name.length > 15 ? 0 : 30;
let desc = description || "No description provided";
if (desc.length > 55) {
desc = `${description.slice(0, 55)}..`;
}
return `
<svg width="400" height="${height}" viewBox="0 0 400 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
.bold { font-weight: 700 }
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: #586069 }
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #586069 }
</style>
<rect x="0.5" y="0.5" width="399" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
<svg x="25" y="25" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
<path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path>
</svg>
<text x="50" y="38" class="header">${name}</text>
<text class="description" x="25" y="70">${encodeHTML(desc)}</text>
<g transform="translate(30, 100)">
<circle cx="0" cy="-5" r="6" fill="${primaryLanguage.color}" />
<text class="gray" x="15">${primaryLanguage.name}</text>
</g>
<g transform="translate(${155 - shiftText}, 100)">
<svg y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
<path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path>
</svg>
<text class="gray" x="25">${kFormatter(stargazers.totalCount)}</text>
</g>
<g transform="translate(${220 - shiftText}, 100)">
<svg y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
<path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
</svg>
<text class="gray" x="25">${kFormatter(forkCount)}</text>
</g>
</svg>
`;
};
const {
renderError,
parseBoolean,
clampValue,
CONSTANTS,
logger,
} = require("../src/utils");
const fetchRepo = require("../src/fetchRepo");
const renderRepoCard = require("../src/renderRepoCard");
module.exports = async (req, res) => {
const { username, repo } = req.query;
const {
username,
repo,
title_color,
icon_color,
text_color,
bg_color,
theme,
show_owner,
cache_seconds,
} = req.query;
let repoData;
res.setHeader("Content-Type", "image/svg+xml");
try {
repoData = await fetchRepo(username, repo);
} catch (err) {
console.log(err);
logger.error(err);
return res.send(renderError(err.message));
}
res.send(renderRepoCard(repoData));
let cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.THIRTY_MINUTES, 10),
CONSTANTS.THIRTY_MINUTES,
CONSTANTS.ONE_DAY
);
/*
if star count & fork count is over 1k then we are kFormating the text
and if both are zero we are not showing the stats
so we can just make the cache longer, since there is no need to frequent updates
*/
const stars = repoData.stargazers.totalCount;
const forks = repoData.forkCount;
const isBothOver1K = stars > 1000 && forks > 1000;
const isBothUnder1 = stars < 1 && forks < 1;
if (!cache_seconds && (isBothOver1K || isBothUnder1)) {
cacheSeconds = CONSTANTS.TWO_HOURS;
}
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
res.send(
renderRepoCard(repoData, {
title_color,
icon_color,
text_color,
bg_color,
theme,
show_owner: parseBoolean(show_owner),
})
);
};
+56
View File
@@ -0,0 +1,56 @@
require("dotenv").config();
const {
renderError,
clampValue,
parseBoolean,
parseArray,
CONSTANTS,
} = require("../src/utils");
const fetchTopLanguages = require("../src/fetchTopLanguages");
const renderTopLanguages = require("../src/renderTopLanguages");
module.exports = async (req, res) => {
const {
username,
hide,
hide_title,
card_width,
title_color,
text_color,
bg_color,
theme,
cache_seconds,
layout
} = req.query;
let topLangs;
res.setHeader("Content-Type", "image/svg+xml");
try {
topLangs = await fetchTopLanguages(username);
} catch (err) {
return res.send(renderError(err.message));
}
const cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.THIRTY_MINUTES, 10),
CONSTANTS.THIRTY_MINUTES,
CONSTANTS.ONE_DAY
);
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
res.send(
renderTopLanguages(topLangs, {
theme,
hide_title: parseBoolean(hide_title),
card_width: parseInt(card_width, 10),
hide: parseArray(hide),
title_color,
text_color,
bg_color,
theme,
layout
})
);
};
+13
View File
@@ -0,0 +1,13 @@
codecov:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
status:
project:
default:
threshold: 5
patch: false
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
clearMocks: true,
};
-46
View File
@@ -1,46 +0,0 @@
{
"name": "github-readme-stats",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"axios": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"dev": true,
"requires": {
"follow-redirects": "1.5.10"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"dev": true,
"requires": {
"debug": "=3.1.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
}
+19 -4
View File
@@ -1,17 +1,32 @@
{
"name": "github-readme-stats",
"version": "1.0.0",
"description": "",
"description": "Dynamically generate stats for your github readmes",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest --coverage",
"test:watch": "jest --watch",
"theme-readme-gen": "node scripts/generate-theme-doc"
},
"author": "Anurag Hazra",
"license": "MIT",
"devDependencies": {
"axios": "^0.19.2"
"@testing-library/dom": "^7.20.0",
"@testing-library/jest-dom": "^5.11.0",
"axios": "^0.19.2",
"axios-mock-adapter": "^1.18.1",
"css-to-object": "^1.1.0",
"husky": "^4.2.5",
"jest": "^26.1.0"
},
"dependencies": {
"dotenv": "^8.2.0"
"dotenv": "^8.2.0",
"emoji-name-map": "^1.2.8",
"word-wrap": "^1.2.3"
},
"husky": {
"hooks": {
"pre-commit": "npm test"
}
}
}
+239 -25
View File
@@ -1,64 +1,150 @@
## Github Readme stats
<p align="center">
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
<h2 align="center">GitHub Readme Stats</h2>
<p align="center">Get dynamically generated GitHub stats on your readmes!</p>
</p>
<p align="center">
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
<img alt="Tests Passing" src="https://github.com/anuraghazra/github-readme-stats/workflows/Test/badge.svg" />
</a>
<a href="https://codecov.io/gh/anuraghazra/github-readme-stats">
<img src="https://codecov.io/gh/anuraghazra/github-readme-stats/branch/master/graph/badge.svg" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/pulls">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<br />
<br />
<a href="https://a.paddle.com/v2/click/16413/119403?link=1227">
<img src="https://img.shields.io/badge/Supported%20by-VSCode%20Power%20User%20%E2%86%92-gray.svg?colorA=655BE1&colorB=4F44D6&style=for-the-badge"/>
</a>
<a href="https://a.paddle.com/v2/click/16413/119403?link=2345">
<img src="https://img.shields.io/badge/Supported%20by-Node%20Cli.com%20%E2%86%92-gray.svg?colorA=61c265&colorB=4CAF50&style=for-the-badge"/>
</a>
</p>
Get dynamically generated GitHub stats on your readmes!
<p align="center">
<a href="#demo">View Demo</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">Report Bug</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">Request Feature</a>
</p>
<p align="center">
<a href="readme_cn.md">简体中文</a>
·
<a href="readme_es.md">Español</a>
·
<a href="readme_de.md">Deutsch</a>
·
<a href="readme_ja.md">日本語</a>
</p>
</p>
<p align="center">Loved the project? Please consider <a href="https://www.paypal.me/anuraghazra">donating</a> to help it improve!
# Features
- [Github Stats Card](#github-stats-card)
- [Github Extra Pins](#github-extra-pins)
- [GitHub Stats Card](#github-stats-card)
- [GitHub Extra Pins](#github-extra-pins)
- [Top Languages Card](#top-languages-card)
- [Themes](#themes)
- [Customization](#customization)
- [Deploy Yourself](#deploy-on-your-own-vercel-instance)
# Github Stats Card
# GitHub Stats Card
Copy paste this into your markdown content, and that's it. Simple!
change the `?username=` value to your GitHubs's username
Change the `?username=` value to your GitHub's username.
```md
[![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
_Note: Ranks are calculated based on user's stats, see [src/calculateRank.js](./src/calculateRank.js)_
### Hiding individual stats
To hide any specific stats, you can pass a query parameter `?hide=` with an array of items, you wanna hide.
To hide any specific stats, you can pass a query parameter `?hide=` with comma separated values.
> Options: `&hide=["stars","prs","issues","contribs"]`
> Options: `&hide=stars,commits,prs,issues,contribs`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["contribs","prs"])
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=contribs,prs)
```
### Adding private contributions count to total commits count
You can add the count of all your private contributions to the total commits count by using the query parameter `?count_private=true`.
_Note: If you are deploying this project yourself, the private contributions will be counted by default otherwise you need to chose to share your private contribution counts._
> Options: `&count_private=true`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&count_private=true)
```
### Showing icons
To enable icons, you can pass `show_icons=true` in the query param like so
To enable icons, you can pass `show_icons=true` in the query param, like so:
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
```
Other options:
### Themes
- `&hide_border=true` hide the border box if you don't like it :D.
- `&line_height=30` control the line-height between text.
With inbuilt themes you can customize the look of the card without doing any [manual customization](#customization).
### Demo
Use `?theme=THEME_NAME` parameter like so :-
- Default
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
```
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)
#### All inbuilt themes :-
- Hiding specific stats
dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontrast, dracula
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["contribs","issues"])
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stat Themes" width="600px"/>
- Showing icons
You can look at a preview for [all available themes](./themes/README.md) or checkout the [theme config file](./themes/index.js) & **you can also contribute new themes** if you like :D
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["issues"]&show_icons=true)
### Customization
# Github Extra Pins
You can customize the appearance of your `Stats Card` or `Repo Card` however you want with URL params.
Github extra pins allow you to pin more than 6 repositories in your profile using a GitHub readme profile.
Customization Options:
Yey! you are no longer limited to 6 pinned repositories.
| Option | type | description | Stats Card (default) | Repo Card (default) | Top Lang Card (default) |
| ------------- | --------- | ------------------------------------------- | -------------------- | ------------------- | ----------------------- |
| title_color | hex color | title color | 2f80ed | 2f80ed | 2f80ed |
| text_color | hex color | body color | 333 | 333 | 333 |
| icon_color | hex color | icon color | 4c71f2 | 586069 | 586069 |
| bg_color | hex color | card bg color | FFFEFE | FFFEFE | FFFEFE |
| line_height | number | control the line-height between text | 30 | N/A | N/A |
| hide | CSV | hides the items specified | undefined | N/A | undefined |
| hide_rank | boolean | hides the ranking | false | N/A | N/A |
| hide_title | boolean | hides the stats title | false | N/A | false |
| hide_border | boolean | hides the stats card border | false | N/A | N/A |
| show_owner | boolean | shows owner name in repo card | N/A | false | N/A |
| show_icons | boolean | shows icons | false | N/A | N/A |
| theme | string | sets inbuilt theme | 'default' | 'default_repocard' | 'default' |
| cache_seconds | number | manually set custom cache control | 1800 | 1800 | 1800 |
| count_private | boolean | counts private contributions too if enabled | false | N/A | N/A |
| layout | string | choose a layout option | N/A | N/A | 'default' |
> Note on cache: Repo cards have default cache of 30mins (1800 seconds) if the fork count & star count is less than 1k otherwise it's 2hours (7200). Also note that cache is clamped to minimum of 30min and maximum of 24hours
# GitHub Extra Pins
GitHub extra pins allow you to pin more than 6 repositories in your profile using a GitHub readme profile.
Yey! You are no longer limited to 6 pinned repositories.
### Usage
@@ -74,9 +160,93 @@ Endpoint: `api/pin?username=anuraghazra&repo=github-readme-stats`
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
Use [show_owner](#customization) variable to include the repo's owner username
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true)](https://github.com/anuraghazra/github-readme-stats)
# Top Languages Card
Top languages card shows github user's top langauges which has been mostly used.
_NOTE: Top languages does not indicate my skill level or something like that, it's a github metric of which languages i have the most code on github, it's a new feature of github-readme-stats_
### Usage
Copy-paste this code into your readme and change the links.
Endpoint: `api/top-langs?username=anuraghazra`
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
### Hide individual languages
You can use `?hide=language1,language2` parameter to hide individual languages.
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide=javascript,html)](https://github.com/anuraghazra/github-readme-stats)
```
> :warning: **Important:**
> Language names should be uri-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding)
> (i.e: `c++` should become `c%2B%2B`, `jupyter notebook` should become `jupyter%20notebook`, etc.)
### Compact Language Card Layout
You can use the `&layout=compact` option to change the card design.
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats)
```
### Demo
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
- Compact layout
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats)
---
### All Demos
- Default
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)
- Hiding specific stats
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=contribs,issues)
- Showing icons
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=issues&show_icons=true)
- Themes
Choose from any of the [default themes](#themes)
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
- Customizing stats card
![Anurag's github stats](https://github-readme-stats.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515)
- Customizing repo card
![Customized Card](https://github-readme-stats.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515)
- Top languages
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
---
### Quick Tip (Align The Repo Cards)
You usually won't be able to layout the images side by side to do that you can use this approach
You usually won't be able to layout the images side by side. To do that you can use this approach:
```md
<a href="https://github.com/anuraghazra/github-readme-stats">
@@ -87,6 +257,50 @@ You usually won't be able to layout the images side by side to do that you can u
</a>
```
## Deploy on your own Vercel instance
Since the GitHub API only allows 5k requests per hour, it is possible that my `https://github-readme-stats.vercel.app/api` could hit the rate limiter. If you host it on your own Vercel server, then you don't have to worry about anything. Click on the deploy button to get started!
NOTE: Since [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) we should be able to handle more than 5k requests and have no issues with downtime :D
[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats)
<details>
<summary>Guide on setting up Vercel</summary>
1. Go to [vercel.com](https://vercel.com/)
1. Click on `Log in`
![](https://files.catbox.moe/tct1wg.png)
1. Sign in with GitHub by pressing `Continue with GitHub`
![](https://files.catbox.moe/btd78j.jpeg)
1. Sign into GitHub and allow access to all repositories, if prompted
1. Fork this repo
1. Go back to your [Vercel dashboard](https://vercel.com/dashboard)
1. Select `Import Project`
![](https://files.catbox.moe/qckos0.png)
1. Select `Import Git Repository`
![](https://files.catbox.moe/pqub9q.png)
1. Select root and keep everything as is, just add your environment variable named PAT_1 (as shown), which will contain a personal access token (PAT), which you can easily create [here](https://github.com/settings/tokens/new) (leave everything as is, just name it something, it can be anything you want)
![](https://files.catbox.moe/0ez4g7.png)
1. Click deploy, and you're good to go. See your domains to use the API!
</details>
## :sparkling_heart: Support the project
I open-source almost everything I can, and I try to reply to everyone needing help using these projects. Obviously,
this takes time. You can use this service for free.
However, if you are using this project and happy with it or just want to encourage me to continue creating stuff, there are few ways you can do it :-
- Giving proper credit when you use github-readme-stats on your readme, linking back to it :D
- Starring and sharing the project :rocket:
- [![paypal.me/anuraghazra](https://ionicabizau.github.io/badges/paypal.svg)](https://www.paypal.me/anuraghazra) - You can make one-time donations via PayPal. I'll probably buy a ~~coffee~~ tea. :tea:
Thanks! :heart:
---
Contributions are welcomed! <3
Made with :heart: and javascript.
Made with :heart: and JavaScript.
+301
View File
@@ -0,0 +1,301 @@
<p align="center">
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
<h2 align="center">GitHub Readme Stats</h2>
<p align="center">在你的 README 中获取动态生成的 GitHub 统计信息!</p>
</p>
<p align="center">
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
<img alt="Tests Passing" src="https://github.com/anuraghazra/github-readme-stats/workflows/Test/badge.svg" />
</a>
<a href="https://codecov.io/gh/anuraghazra/github-readme-stats">
<img src="https://codecov.io/gh/anuraghazra/github-readme-stats/branch/master/graph/badge.svg" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/pulls">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<br />
<br />
<a href="https://a.paddle.com/v2/click/16413/119403?link=1227">
<img src="https://img.shields.io/badge/Supported%20by-VSCode%20Power%20User%20%E2%86%92-gray.svg?colorA=655BE1&colorB=4F44D6&style=for-the-badge"/>
</a>
<a href="https://a.paddle.com/v2/click/16413/119403?link=2345">
<img src="https://img.shields.io/badge/Supported%20by-Node%20Cli.com%20%E2%86%92-gray.svg?colorA=61c265&colorB=4CAF50&style=for-the-badge"/>
</a>
</p>
<p align="center">
<a href="#demo">查看 Demo</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">报告 bug</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">请求增加功能</a>
</p>
<p align="center">
<a href="readme.md">English</a>
·
<a href="readme_es.md">Español</a>
·
<a href="readme_ja.md">日本語</a>
</p>
</p>
<p align="center">喜欢这个项目?请考虑<a href="https://www.paypal.me/anuraghazra">捐赠</a>来帮助它完善!
# 特性
- [GitHub 统计卡片](#GitHub-统计卡片)
- [GitHub 更多置顶](#GitHub-更多置顶)
- [热门语言卡片](#热门语言卡片)
- [主题](#主题)
- [自定义](#自定义)
- [自己部署](#自己部署)
# GitHub 统计卡片
将这行代码复制到你的 markdown 文件中,简单如此!
更改 `?username=` 的值为你的 GitHub 用户名。
```md
[![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
_注: 排名基于用户的统计信息计算得出,详见 [src/calculateRank.js](./src/calculateRank.js)_
### 隐藏个别统计项
想要隐藏指定统计信息,你可以调用参数 `?hide=`,其值用 `,` 分隔。
> 选项:`&hide=stars,commits,prs,issues,contribs`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=contribs,prs)
```
### 把私人贡献计数添加到总提交计数中
你可以用参数 `?count_private=true` 把私人贡献计数添加到总提交计数中。
_注:如果你是自己部署本项目,私人贡献将会默认被计数,如果不是自己部署,你需要分享你的私人贡献计数。_
> Options: `&count_private=true`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&count_private=true)
```
### 显示图标
想要显示图标,你可以调用 `show_icons=true` 参数,如下:
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
```
### 主题
你可以通过现有的主题进行卡片个性化,省去[手动自定义](#自定义)的麻烦。
调用 `?theme=THEME_NAME` 参数,如下:
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
```
#### 所有现有主题
dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontrast, dracula
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stat Themes" width="600px"/>
在 [theme config 文件](./themes/index.js) 中查看更多主题,或者 **贡献新的主题** :D
### 自定义
你可以通过使用 URL 参数的方式,为你的 `Stats Card` 或 `Repo Card` 自定义样式。
自定义选项:
| Option | type | description | Stats Card (default) | Repo Card (default) | Top Lang Card (default) |
| ------------- | --------- | ---------------------------- | -------------------- | ------------------- | ----------------------- |
| title_color | hex color | 标题颜色 | 2f80ed | 2f80ed | 2f80ed |
| text_color | hex color | 字体颜色 | 333 | 333 | 333 |
| icon_color | hex color | 图标颜色 | 4c71f2 | 586069 | 586069 |
| bg_color | hex color | 卡片背景颜色 | FFFEFE | FFFEFE | FFFEFE |
| line_height | number | 文字行高 | 30 | N/A | N/A |
| hide | CSV | 隐藏指定统计项 | undfined | N/A | undefined |
| hide_rank | boolean | 隐藏评分等级 | false | N/A | N/A |
| hide_title | boolean | 隐藏卡片标题 | false | N/A | false |
| hide_border | boolean | 隐藏卡片边框 | false | N/A | N/A |
| show_owner | boolean | 显示 Repo 卡片所属账户用户名 | N/A | false | N/A |
| show_icons | boolean | 显示图标 | false | N/A | N/A |
| theme | string | 设置主题 | 'default' | 'default_repocard' | 'default' |
| cache_seconds | number | 手动设置自定义缓存控制 | 1800 | 1800 | 1800 |
| count_private | boolean | 统计私人贡献计数 | false | N/A | N/A |
| layout | string | 布局方式 | N/A | N/A | 'default' |
> 注意缓存:Repo 卡片默认缓存 30 分钟,如果 fork 数和 star 数小于 1k ,则默认为 2 小时。缓存被限制为最少 30 分钟,最长 24 小时。
# GitHub 更多置顶
GitHub 更多置顶 让你使用 README Profile,在个人页面中置顶多于 6 个 repo 。
这波可以!你再也不用受限于最多 6 个置顶了。
### 使用细则
复制粘贴这段代码到你的 README 文件中,并更改链接。
Endpoint: `api/pin?username=anuraghazra&repo=github-readme-stats`
```md
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
```
### Demo
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
使用 [show_owner](#自定义) 变量将 Repo 所属账户的用户名包含在内。
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true)](https://github.com/anuraghazra/github-readme-stats)
# 热门语言卡片
热门语言卡片显示了 GitHub 用户常用的编程语言。
_注意:热门语言并不表示我的技能水平或类似的水平,它是用户在 GitHub 上拥有最多代码的一项指标,它是 github-readme-stats 的新功能_
### 使用细则
将此代码复制粘贴到您的`README.md`文件中,并改变链接。
Endpoint: `api/top-langs?username=anuraghazra`
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
### 隐藏特定语言
可以使用`?hide=语言1,语言2`参数来隐藏指定的语言。
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide=语言1,语言2)](https://github.com/anuraghazra/github-readme-stats)
```
### 紧凑的语言卡片布局
你可以使用 `&layout=compact` 参数来改变卡片的样式。
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats)
```
### Demo
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
- 紧凑布局
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats)
---
### 全部 Demo
- 默认
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)
- 隐藏特定数据
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=contribs,issues)
- 显示图标
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=issues&show_icons=true)
- 主题
从 [默认主题](#主题) 中进行选择
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
- 自定义统计卡片
![Anurag's github stats](https://github-readme-stats.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515)
- 自定义代码库卡片
![Customized Card](https://github-readme-stats.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515)
- 热门语言
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
---
### 提示 (对齐 Repo 卡片)
你通常无法将图片靠边显示。为此,您可以使用以下方法:
```md
<a href="https://github.com/anuraghazra/github-readme-stats">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats" />
</a>
<a href="https://github.com/anuraghazra/convoychat">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=convoychat" />
</a>
```
## 自己部署
因为 GitHub 的 API 每个小时只允许 5 千次请求,我的 `https://github-readme-stats.vercel.app/api` 很有可能会触发限制
如果你将其托管在自己的 Vercel 服务器上,那么你就不必为此担心。点击 deploy 按钮来开始你的部署!
注意: 从 [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) 开始,我们应该能够处理超过 5 千 的请求,并且不会出现宕机问题 :D
[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats)
<details>
<summary>设置 Vercel 的指导</summary>
1. 前往 [vercel.com](https://vercel.com/)
1. 点击 `Log in`
![](https://files.catbox.moe/tct1wg.png)
1. 点击 `Continue with GitHub` 通过 GitHub 进行登录
![](https://files.catbox.moe/btd78j.jpeg)
1. 登录 GitHub 并允许访问所有存储库(如果系统这样提示)
1. Fork 这个仓库
1. 返回到你的 [Vercel dashboard](https://vercel.com/dashboard)
1. 选择 `Import Project`
![](https://files.catbox.moe/qckos0.png)
1. 选择 `Import Git Repository`
![](https://files.catbox.moe/pqub9q.png)
1. 选择 root 并将所有内容保持不变,并且只需添加名为 PAT_1 的环境变量(如图所示),其中将包含一个个人访问令牌(PAT),你可以在[这里](https://github.com/settings/tokens/new)轻松创建(保留默认,并且只需要命名下,名字随便)
![](https://files.catbox.moe/caem5b.png)
1. 点击 deploy,这就完成了,查看你的域名就可使用 API 了!
</details>
## :sparkling_heart: 支持这个项目
我尽己所能地进行开源,并且我尽量回复每个在使用项目时需要帮助的人。很明显,这需要时间,但你可以免费享受这些。
然而, 如果你正在使用这个项目并感觉良好,或只是想要支持我继续开发,你可以通过如下方式:
- 在你的 README 中使用 github-readme-stats 时,链接指向会这里 :D
- Star 并 分享这个项目 :rocket:
- [![paypal.me/anuraghazra](https://ionicabizau.github.io/badges/paypal.svg)](https://www.paypal.me/anuraghazra) - 你可以通过 PayPal 一次性捐款. 我多半会买一杯 ~~咖啡~~ 茶。:tea:
谢谢! :heart:
---
欢迎贡献! <3
用 :heart: 发电,用 JavaScript 制作。
+268
View File
@@ -0,0 +1,268 @@
<p align="center">
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
<h2 align="center">GitHub Readme Stats</h2>
<p align="center">Zeige dynamisch generierte GitHub-Statistiken in deinen Readmes!</p>
</p>
<p align="center">
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
<img alt="Tests Passing" src="https://github.com/anuraghazra/github-readme-stats/workflows/Test/badge.svg" />
</a>
<a href="https://codecov.io/gh/anuraghazra/github-readme-stats">
<img src="https://codecov.io/gh/anuraghazra/github-readme-stats/branch/master/graph/badge.svg" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/pulls">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<br />
<br />
<a href="https://a.paddle.com/v2/click/16413/119403?link=1227">
<img src="https://img.shields.io/badge/Supported%20by-VSCode%20Power%20User%20%E2%86%92-gray.svg?colorA=655BE1&colorB=4F44D6&style=for-the-badge"/>
</a>
<a href="https://a.paddle.com/v2/click/16413/119403?link=2345">
<img src="https://img.shields.io/badge/Supported%20by-Node%20Cli.com%20%E2%86%92-gray.svg?colorA=61c265&colorB=4CAF50&style=for-the-badge"/>
</a>
</p>
<p align="center">
<a href="#alle-beispiele">Beispiel ansehen</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues">Fehler melden</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues">Funktionalität anfragen</a>
</p>
</p>
<p align="center">Du magst das Projekt? Wie wäre es mit einer kleinen <a href="https://www.paypal.me/anuraghazra">Spende</a> um es weiterhin am Leben zu erhalten?
# Funktionalitäten
- [GitHub Stats Card](#github-stats-card)
- [GitHub Extra Pins](#github-extra-pins)
- [Top Programmiersprachen Card](#top-programmiersprachen-card)
- [Erscheinungsbild/Themes](#erscheinungsbildthemes)
- [Anpassungen](#anpassungenpersonalisierung)
- [Selber betreiben](#betreibe-es-auf-deiner-eigenen-vercel-instanz)
# GitHub Stats Card
Kopiere einfach folgendes in dein Markdown und das wars. Echt simpel!
Passe den Wert des URL-Paramters `?username=` so an, dass dort dein GitHub Username steht.
```md
[![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
_Hinweis: Die Berechnung des Ranges basiert auf den jeweiligen Benutzerstatistiken, siehe [src/calculateRank.js](./src/calculateRank.js)_
### Verbergen individueller Statistiken
Um eine spezifische Statistik auszublenden, kann dem Query-Parameter `?hide=` ein Array an Dingen die nicht angezeigt werden sollen übergeben werden.
> Optionen: `&hide=["stars","commits","prs","issues","contribs"]`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["contribs","prs"])
```
### Icons anzeigen
Um Icons anzuzeigen kann der URL-Paramter `show_icons=true` wie folgt verwendet werden:
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
```
### Erscheinungsbild/Themes
Mithilfe der eingebauten Themes kann das Aussehen der Karten verändern werden ohne manuelle Anpassungen vornehmen zu müssen.
Benutze den `?theme=THEME_NAME`-Parameter wie folgt :-
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
```
#### Alle eingebauten Themes :-
dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontrast, dracula
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stat Themes" width="600px"/>
Du kannst dir eine Vorschau [aller verfügbaren Themes](./themes/README.md) ansehen oder die [theme config Datei](./themes/index.js) auschecken.
Außerdem **kannst du neue Themes beisteuern** wenn du möchtest, contributions sind gern gesehen :D
### Anpassungen/Personalisierung
Du kannst das Erscheinungsbild deiner `Stats Card` oder `Repo Card`, mithilfe von URL-Parametern, nach deinen Vorlieben anpassen.
Anpassungsoptionen:
| Option | Type | Beschreibung | Statistiken (default) | Repo (default) | Top Programmiersprachen (default) |
| ---------------- | --------- | ---------------------------------------------------- | --------------------- | ------------------ | --------------------------------- |
| title_color | hex color | Titelfarbe | 2f80ed | 2f80ed | 2f80ed |
| text_color | hex color | Textkörperfarbe | 333 | 333 | 333 |
| icon_color | hex color | Iconfarbe | 4c71f2 | 586069 | 586069 |
| bg_color | hex color | Hintergrundfarbe | FFFEFE | FFFEFE | FFFEFE |
| line_height | number | kontrolliert die Zeilenhöhe zwischen dem Text | 30 | N/A | N/A |
| hide_rank | boolean | blendet das Ranking aus | false | N/A | N/A |
| hide_title | boolean | blendet den Statistik-Titel aus | false | N/A | false |
| hide_border | boolean | blendet den Rahmen aus | false | N/A | N/A |
| show_owner | boolean | zeigt den Namen des Besitzers in der Repo-Karte | N/A | false | N/A |
| show_icons | boolean | zeige Icons an | false | N/A | N/A |
| theme | string | setze eingebaute themes | 'default' | 'default_repocard' | 'default' |
| cache_seconds | number | manuelles setzen der Cachezeiten | 1800 | 1800 | 1800 |
| hide_langs_below | number | verberge Sprachen unter einem bestimmten Schwellwert | N/A | N/A | undefined |
> Hinweis bzgl. des Caches: Wenn die Anzahl der Forks und Stars geringer als 1Tsd ist, haben die Repo-Cards eine default Cachezeit von 30 Minuten (1800 Sekunden), ansonsten beträgt diese 2 Stunden (7200 Sekunden). Außerdem ist der Cache auf eine Minimum von 30 Minuten und ein Maximum von 24 Stunden begrenzt.
# GitHub Extra Pins
GitHub extra pins ermöglicht es, mit Hilfe eines GitHub-Readme-Profiles, mehr als 6 Repositories in deinen Profil anzuzeigen.
Und Bääm! Du bist nicht mehr auf 6 pinned Repositories limitiert.
### Benutzung
Füge diesen Code in deine Readme-Datei ein und passe die Links an.
Endpunkt: `api/pin?username=anuraghazra&repo=github-readme-stats`
```md
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
```
### Beispiele
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
Benutze die [show_owner](#anpassungenpersonalisierung) Variable, um den Usernamen des Repo Eigentümers anzuzeigen.
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true)](https://github.com/anuraghazra/github-readme-stats)
# Top Programmiersprachen Card
Die Top Programmiersprachen Card visualisiert die am meisten benutzten Programmiersprachen eines GitHub-Nutzers.
_HINWEIS: Die Top Programmiersprachen treffen keine Aussage über persönliche Fähigkeiten oder der gleichen, es ist lediglich eine auf den GitHub-Statistiken des Nutzers basierende Kennzahl welche Programmiersprache wie häufig verwendet wurde._
### Benutzung
Füge diesen Code in deine Readme-Datei ein und passe die Links an.
Endpunkt: `api/top-langs?username=anuraghazra`
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
### Verberge Programmiersprachen unter einem bestimmten Schwellwert
Benutze den `?hide_langs_below=NUMBER` URL-Parameter um Programmiersprachen unter einem bestimmten prozentualen Schwellwert auszublenden.
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide_langs_below=1)](https://github.com/anuraghazra/github-readme-stats)
```
### Beispiel
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
---
### Alle Beispiele
- Default
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)
- Ausblenden bestimmter Statistiken
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["contribs","issues"])
- Icons anzeigen
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["issues"]&show_icons=true)
- Erscheinungsbild/Themes
Choose from any of the [default themes](#themes)
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
- Stats Card anpassen
![Anurag's github stats](https://github-readme-stats.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515)
- Repo Card anpassen
![Customized Card](https://github-readme-stats.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515)
- Top Programmiersprachen
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
---
### Kleiner Tip (Ausrichten der Repo Cards)
Üblicherweise ist es in `.md`-Dateien nicht möglich Bilder nebeneinander anzuzeigen. Um dies zu erreichen kann folgender Ansatz gewählt werden:
```md
<a href="https://github.com/anuraghazra/github-readme-stats">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats" />
</a>
<a href="https://github.com/anuraghazra/convoychat">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=convoychat" />
</a>
```
## Betreibe es auf deiner eigenen Vercel-Instanz
Da die GitHub API nur 5tsd Aufrufe pro Stunde zulässt, kann es passieren, dass meine `https://github-readme-stats.vercel.app/api` dieses Limit erreicht.
Wenn du es auf deinem eigenen Vercel-Server hostest, brauchst du dich darum nicht zu kümmern. Klicke auf den Deploy-Button um loszulegen!
Hinweis: Seit [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) sollte es möglich sein mehr als 5tsd Aufrufe pro Stunde ohne Downtimes zu verkraften :D
[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats)
<details>
<summary>Anleitung zum Einrichten von Vercel</summary>
1. Gehe zu [vercel.com](https://vercel.com/)
1. Klicke auf `Log in`
![](https://files.catbox.moe/tct1wg.png)
1. Melde dich mit deinem GitHub-account an, indem du `Continue with GitHub` klickst
![](https://files.catbox.moe/btd78j.jpeg)
1. Verbinde dich mit GitHub und erlaube den Zugriff auf alle Repositories, wenn gefordert
1. Forke dieses Repository
1. Gehe zurück zu deinem [Vercel dashboard](https://vercel.com/dashboard)
1. Klick `Import Project`
![](https://files.catbox.moe/qckos0.png)
1. Klick `Import Git Repository`
![](https://files.catbox.moe/pqub9q.png)
1. Wähle root und füge eine Umgebungsvariable namens PAT_1 (siehe Abbildung) die als Wert deinen persönlichen Access Token (PAT) hat hinzu, den du einfach [hier](https://github.com/settings/tokens/new) erzeugen kannst (lasse alles wie es ist, vergebe einen beliebigen Namen)
![](https://files.catbox.moe/0ez4g7.png)
1. Klicke deploy, und das wars. Besuche deine domains um die API zu benutzen!
</details>
## :sparkling_heart: Unterstütze das Projekt
Ich versuche alles was ich kann als Open-Source zur Verfügung zu stellen, als auch jedem der Hilfe bei der Benutzung dieses Projektes braucht zu antworten. Natürlich beansprucht sowas Zeit und Du kannst diesen Dienst kostenlos benutzen.
Dennoch, wenn Du dieses Projekt benutzt und damit zufrieden bist oder mich einfach nur motivieren möchtest weiterhin daran zu arbeiten, gibt es verschiedene Sachen die Du machen kannst:-
- Erwähne und verlinke das Projekt in deiner Readme wenn du es benutzt :D
- Geb dem Projekt einen Stern hier auf GitHub und teile es :rocket:
- [![paypal.me/anuraghazra](https://ionicabizau.github.io/badges/paypal.svg)](https://www.paypal.me/anuraghazra) - Du kannst einmalige Spenden via PayPal tätigen. Ich kaufe mir wahrscheinlich einen ~~Kaffee~~ Tee davon. :tea:
Vielen Dank! :heart:
---
Mitarbeit an dem Projekt is immer Willkommen! <3
Gebaut mit :heart: und JavaScript.
+247
View File
@@ -0,0 +1,247 @@
<p align="center">
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
<h2 align="center">GitHub Readme Stats</h2>
<p align="center">¡Obtén tus estadísticas de GitHub generadas dinámicamente en tu README!</p>
</p>
<p align="center">
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
<img alt="Tests Passing" src="https://github.com/anuraghazra/github-readme-stats/workflows/Test/badge.svg" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/pulls">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<br />
<br />
<a href="https://a.paddle.com/v2/click/16413/119403?link=1227">
<img src="https://img.shields.io/badge/Supported%20by-VSCode%20Power%20User%20%E2%86%92-gray.svg?colorA=655BE1&colorB=4F44D6&style=for-the-badge"/>
</a>
<a href="https://a.paddle.com/v2/click/16413/119403?link=2345">
<img src="https://img.shields.io/badge/Supported%20by-Node%20Cli.com%20%E2%86%92-gray.svg?colorA=61c265&colorB=4CAF50&style=for-the-badge"/>
</a>
</p>
<p align="center">
<a href="#ejemplo">Ve un ejemplo</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">Reporta un bug</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">Solicita una mejora</a>
</p>
<p align="center">
<a href="readme.md">English</a>
·
<a href="readme_cn.md">简体中文</a>
·
<a href="readme_ja.md">日本語</a>
</p>
</p>
<p align="center">¿Te gusta este proyecto? ¡Por favor considera <a href="https://www.paypal.me/anuraghazra">donar</a> para ayudar a mejorarlo!
# Características
- [Tarjeta de estadísticas de GitHub](#tarjeta-de-estadísticas-de-github)
- [Pins extra de GitHub](#pins-extra-de-github)
- [Temas](#temas)
- [Personalización](#personalización)
- [Despliega por tu cuenta](#despliega-tu-propia-instancia-de-vercel)
# Tarjeta de estadísticas de GitHub
Copia y pega esto en el contenido de tu README.md y listo. ¡Simple!
Cambia el valor `?username=` al nombre de tu usuario de GitHub.
```md
[![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
_Nota: las clasificaciones se calculan basándose en las estadísticas del usuario. Ve [src/calculateRank.js](./src/calculateRank.js)._
### Ocultar estadísticas individualmente
Para ocultar alguna estadística específica, puedes utilizar el parámetro `?hide=` con un arreglo de items que quieras ocultar.
> Opciones: `&hide=["stars","commits","prs","issues","contribs"]`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["contribs","prs"])
```
### Agregar contribuciones privadas al total de commits contados
Puede agregar el recuento de todas sus contribuciones privadas al recuento total de confirmaciones utilizando el parámetro de consulta `?count_private=true`.
_Nota: Si está desplegando este proyecto usted mismo, las contribuciones privadas se contarán de manera predeterminada; de lo contrario, deberá elegir compartir sus recuentos de contribuciones privadas._
> Opciones: `&count_private=true`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&count_private=true)
```
### Mostrar íconos
Para habilitar los íconos, puedes utilizar `show_icons=true` como parámetro, de esta manera:
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
```
### Temas
Puedes personalizar el aspecto de la tarjeta sin realizar ninguna [personalización manual](#personalización) con los temas incorporados.
Utiliza el parámetro `?theme=THEME_NAME`, de esta manera:
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
```
#### Todos los temas incorporados
dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontrast, dracula
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stat Themes" width="600px"/>
Puedes ver una vista previa de [todos los temas disponibles](./themes/README.md) o ver el [archivo de configuración](./themes/index.js) del tema y también **puedes contribuir con nuevos temas** si lo deseas: D
### Personalización
Puedes personalizar el aspecto de tu `Stats Card` o `Repo Card` de la manera que desees con los parámetros URL.
Opciones de personalización:
| Option | type | description | Stats Card (default) | Repo Card (default) |
| ----------- | --------- | ----------------------------- | -------------------- | ------------------- |
| title_color | hex color | color del título | 2f80ed | 2f80ed |
| text_color | hex color | color del contenido | 333 | 333 |
| icon_color | hex color | color del ícono | 4c71f2 | 586069 |
| bg_color | hex color | color de fondo | FFFEFE | FFFEFE |
| line_height | number | controla el line_height | 30 | N/A |
| hide_rank | boolean | oculta la clasificación | false | N/A |
| hide_title | boolean | oculta el título | false | N/A |
| hide_border | boolean | oculta el borde | false | N/A |
| show_owner | boolean | muestra el propietario | N/A | false |
| show_icons | boolean | muestra los íconos | false | N/A |
| theme | string | establece un tema incorporado | 'default' | 'default_repocard' |
---
### Ejemplo
- Predeterminado
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)
- Ocultando estadísticas específicas
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["contribs","issues"])
- Mostrando íconos
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=["issues"]&show_icons=true)
- Temas
Elige uno de los [temas predeterminados](#temas)
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
- Personalizando la tarjeta de estadísticas
![Anurag's github stats](https://github-readme-stats.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515)
- Personalizando la tarjeta de repositorio
![Customized Card](https://github-readme-stats.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515)
---
# Pins extra de GitHub
Los pins extra de GitHub te permiten anclar más de 6 repositorios en tu perfil utilizando el archivo README.md.
¡Bien! Ya no estás limitado a 6 repositorios anclados.
### Utilización
Copia y pega este código en tu README.md y cambia los links.
Endpoint: `api/pin?username=anuraghazra&repo=github-readme-stats`
```md
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
```
### Ejemplo
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
Utiliza la variable [show_owner](#customización) para incluir el nombre de usuario del propietario del repositorio.
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true)](https://github.com/anuraghazra/github-readme-stats)
### Pequeño consejo (alinear las tarjetas de repositorios)
Usualmente no serías capaz de alinear las imágenes una al lado de otra. Para lograrlo, puedes realizar esto:
```md
<a href="https://github.com/anuraghazra/github-readme-stats">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats" />
</a>
<a href="https://github.com/anuraghazra/convoychat">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=convoychat" />
</a>
```
## Despliega tu propia instancia de vercel
Desde que la API de GitHub permite solo 5 mil peticiones por hora, es posible que mi `https://github-readme-stats.vercel.app/api` pueda llegar al límite. Si lo alojas en tu propio servidor de Vercel, no tendrás que preocuparte de nada. ¡Clickea en el botón "Deploy" para comenzar!
Nota: debido a esto [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) podríamos manejar más de 5 mil peticiones sin tener ningún problema con el downtime :D
[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats)
<details>
<summary>Guía para comenzar en Vercel</summary>
1. Ve a [vercel.com](https://vercel.com/)
1. Clickea en `Log in`
![](https://files.catbox.moe/tct1wg.png)
1. Inicia sesión con GitHub presionando `Continue with GitHub`
![](https://files.catbox.moe/btd78j.jpeg)
1. Permite el acceso a todos los repositorios (si se te pregunta)
1. Haz un Fork de este repositorio
1. Dirígete de nuevo a tu [Vercel dashboard](https://vercel.com/dashboard)
1. Selecciona `Import Project`
![](https://files.catbox.moe/qckos0.png)
1. Selecciona `Import Git Repository`
![](https://files.catbox.moe/pqub9q.png)
1. Selecciona "root" y matén todo como está, simplemente añade tu variable de entorno llamada PAT_1 (como se muestra), la cual contendrá un token de acceso personal (PAT), el cual puedes crear fácilmente [aquí](https://github.com/settings/tokens/new) (mantén todo como está, simplemente asígnale un nombre, puede ser cualquiera que desees)
![](https://files.catbox.moe/caem5b.png)
1. Clickea "Deploy" y ya está listo. ¡Ve tus dominios para usar la API!
</details>
## :sparkling_heart: Apoya al proyecto
Casi todos mis proyectos son código-abierto e intento responder a todos los usuarios que necesiten ayuda con alguno de estos proyectos, Obviamente,
esto toma tiempo. Puedes usar este servicio gratis.
No obstante, si estás utilizando este proyecto y estás feliz con él o simplemente quieres animarme a que siga creando cosas, aquí tienes algunas maneras de hacerlo:
- Darme créditos cuando estés utilizando github-readme-stats en tu README, añadiendo un link a este repositorio :D
- Dándole una estrella (starring) y compartiendo el proyecto :rocket:
- [![paypal.me/anuraghazra](https://ionicabizau.github.io/badges/paypal.svg)](https://www.paypal.me/anuraghazra) - Puedes hacerme una única donación a través de PayPal. Probablemente me compraré un ~~café~~ té. :tea:
¡Gracias! :heart:
---
¡Las contribuciones son bienvenidas! <3
Hecho con :heart: y JavaScript.
+302
View File
@@ -0,0 +1,302 @@
<p align="center">
<img width="100px" src="https://res.cloudinary.com/anuraghazra/image/upload/v1594908242/logo_ccswme.svg" align="center" alt="GitHub Readme Stats" />
<h2 align="center">GitHub Readme Stats</h2>
<p align="center">あなたのREADMEに動的に生成されたGitHubの統計情報を載せましょう!</p>
</p>
<p align="center">
<a href="https://github.com/anuraghazra/github-readme-stats/actions">
<img alt="Tests Passing" src="https://github.com/anuraghazra/github-readme-stats/workflows/Test/badge.svg" />
</a>
<a href="https://codecov.io/gh/anuraghazra/github-readme-stats">
<img src="https://codecov.io/gh/anuraghazra/github-readme-stats/branch/master/graph/badge.svg" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<a href="https://github.com/anuraghazra/github-readme-stats/pulls">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/anuraghazra/github-readme-stats?color=0088ff" />
</a>
<br />
<br />
<a href="https://a.paddle.com/v2/click/16413/119403?link=1227">
<img src="https://img.shields.io/badge/Supported%20by-VSCode%20Power%20User%20%E2%86%92-gray.svg?colorA=655BE1&colorB=4F44D6&style=for-the-badge"/>
</a>
<a href="https://a.paddle.com/v2/click/16413/119403?link=2345">
<img src="https://img.shields.io/badge/Supported%20by-Node%20Cli.com%20%E2%86%92-gray.svg?colorA=61c265&colorB=4CAF50&style=for-the-badge"/>
</a>
</p>
<p align="center">
<a href="#demo">View Demo</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">Report Bug</a>
·
<a href="https://github.com/anuraghazra/github-readme-stats/issues/new/choose">Request Feature</a>
</p>
<p align="center">
<a href="readme.md">English</a>
·
<a href="readme_cn.md">简体中文</a>
·
<a href="readme_es.md">Español</a>
</p>
</p>
<p align="center">このプロジェクトを気に入っていただけましたか?<br>もしよろしければ、プロジェクトのさらなる改善のために<a href="https://www.paypal.me/anuraghazra">寄付</a>を検討して頂けると嬉しいです!</p>
# Features
- [GitHub Stats Card](#github-stats-card)
- [GitHub Extra Pins](#github-extra-pins)
- [Top Languages Card](#top-languages-card)
- [Themes](#themes)
- [Customization](#customization)
- [Deploy Yourself](#deploy-on-your-own-vercel-instance)
# GitHub Stats Card
以下の構文をコピーして、あなたの Markdown ファイルに貼り付けるだけです。
簡単ですね!
`?username=` の値は、あなたの GitHub アカウントのユーザー名に変更してください。
```md
[![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
_Note: カードに表示されるランクはユーザの統計情報に基づいて計算されています。詳しくは、[src/calculateRank.js](./src/calculateRank.js)を見てください。_
### Hiding individual stats
クエリパラメータ `?hide=` にカンマ区切りの値を渡すことで、特定の統計情報を隠すことができます。
> Options: `&hide=stars,commits,prs,issues,contribs`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=contribs,prs)
```
### Adding private contributions count to total commits count
クエリパラメータ `?count_private=true` を使用することで、private contributions の数をコミット総数に追加することができます。
_Note: このプロジェクトを自分でデプロイしている場合、デフォルトでは非公開の貢献がカウントされます。_
> Options: `&count_private=true`
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&count_private=true)
```
### Showing icons
クエリパラメータ `?show_icons=true` を使用することで、アイコンが表示が有効になります。
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
```
### Themes
内蔵されているテーマを使用することで、任意の[手動のカスタマイズ](#customization)を行うことなく、カードの外観をカスタマイズすることができます。
`?theme=THEME_NAME` は以下のように使います。
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
```
#### All inbuilt themes :-
dark, radical, merko, gruvbox, tokyonight, onedark, cobalt, synthwave, highcontrast, dracula
<img src="https://res.cloudinary.com/anuraghazra/image/upload/v1595174536/grs-themes_l4ynja.png" alt="GitHub Readme Stat Themes" width="600px"/>
用意されている全てのテーマの[プレビュー](./themes/README.md)や[設定ファイル](./themes/index.js)を見ることができます。もしよろしければ、**新しいテーマを投稿してみてください** (´∀` )
### Customization
`Stats Card` や `Repo Card` の外観を URL パラメーターを使って好きなようにカスタマイズすることができます。
Customization Options:
| Option | type | description | Stats Card (default) | Repo Card (default) | Top Lang Card (default) |
| ------------- | --------- | -------------------------------------------- | -------------------- | ------------------- | ----------------------- |
| title_color | hex color | タイトルの色 | 2f80ed | 2f80ed | 2f80ed |
| text_color | hex color | 文字の色 | 333 | 333 | 333 |
| icon_color | hex color | アイコンの色 | 4c71f2 | 586069 | 586069 |
| bg_color | hex color | カードの背景色 | FFFEFE | FFFEFE | FFFEFE |
| line_height | number | 字間距離 | 30 | N/A | N/A |
| hide | CSV | 項目の非表示 | undefined | N/A | undefined |
| hide_rank | boolean | ranking の非表示 | false | N/A | N/A |
| hide_title | boolean | タイトルの非表示 | false | N/A | false |
| hide_border | boolean | 枠線の非表示 | false | N/A | N/A |
| show_owner | boolean | オーナー名の表示 | N/A | false | N/A |
| show_icons | boolean | アイコンの表示 | false | N/A | N/A |
| theme | string | 用意されているテーマ | 'default' | 'default_repocard' | 'default' |
| cache_seconds | number | キャッシュコントロール | 1800 | 1800 | 1800 |
| count_private | boolean | private contributions 数をコミット総数に追加 | false | N/A | N/A |
| layout | string | レイアウトのオプション選択 | N/A | N/A | 'default' |
> キャッシュに関する注意点: Repo cards のデフォルトのキャッシュは、フォーク数とスター数が 1k 未満の場合は 30 分(1800 秒) で、それ以外の場合は 2 時間(7200) です。また、キャッシュは最低でも 30 分、最大でも 24 時間に制限されていることに注意してください。
# GitHub Extra Pins
GitHub extra pins を使うと、GitHub の readme プロフィールを使って、自分のプロフィールに 6 つ以上のリポジトリをピン留めすることができます。
イェーイ! もはや 6 つのピン留めされたリポジトリに制限されることはありません。
### Usage
以下のコードをあなたの readme にコピー & ペーストし、リンクを変更してください。
Endpoint: `api/pin?username=anuraghazra&repo=github-readme-stats`
```md
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
```
### Demo
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats)
リポジトリのオーナーのユーザー名を含める場合は、show_owner 変数を使用します。
[![ReadMe Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true)](https://github.com/anuraghazra/github-readme-stats)
# Top Languages Card
Top languages card には、その GitHub ユーザーが最も利用している Top languages が表示されます。
_NOTE: Top languages は、ユーザのスキルレベルを示すものではなく、GitHub 上でどの言語で最も多くのコードを書いているかを示す GitHub の指標です。_
### Usage
以下のコードをあなたの readme にコピー & ペーストし、リンクを変更してください。
Endpoint: `api/top-langs?username=anuraghazra`
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
```
### Hide individual languages
クエリパラメータ `?hide=language1,language2` 使用することで、個々の言語を非表示にすることができます。
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide=javascript,html)](https://github.com/anuraghazra/github-readme-stats)
```
### Compact Language Card Layout
クエリパラメータ `&layout=compact` を使用することで、カードのデザインを変更することができます。
```md
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats)
```
### Demo
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
- Compact layout
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats)
---
### All Demos
- Default
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra)
- Hiding specific stats
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=contribs,issues)
- Showing icons
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=issues&show_icons=true)
- Themes
任意の[テーマ](#themes)を選択できます。
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical)
- Customizing stats card
![Anurag's github stats](https://github-readme-stats.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515)
- Customizing repo card
![Customized Card](https://github-readme-stats.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515)
- Top languages
[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats)
---
### Quick Tip (Align The Repo Cards)
通常、画像を並べてレイアウトすることはできません。そのためには、次のような方法があります。
```md
<a href="https://github.com/anuraghazra/github-readme-stats">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats" />
</a>
<a href="https://github.com/anuraghazra/convoychat">
<img align="left" src="https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=convoychat" />
</a>
```
## Deploy on your own Vercel instance
GitHub API は 1 時間あたり 5k リクエストしか受け付けていないので、私の `https://github-readme-stats.vercel.app/api` がレートリミッターを超えてしまう可能性があります。自分の Vercel サーバーでホストしているのであれば、何も心配する必要はありません。デプロイボタンをクリックして始めましょう!
NOTE: [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) 以降は 5k 以上のリクエストに対応できるようになり、ダウンタイムの問題もなくなりました (´∀` )
[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats)
<details>
<summary>Vercelの設定ガイド</summary>
1. [vercel.com](https://vercel.com/)に行きます。
1. `Log in`をクリックします。
![](https://files.catbox.moe/tct1wg.png)
1. `Continue with GitHub` を押して GitHub にサインインします。
![](https://files.catbox.moe/btd78j.jpeg)
1. GitHub にサインインし、すべてのリポジトリへのアクセスを許可します。
1. このリポジトリをフォークします。
1. [Vercel dashboard](https://vercel.com/dashboard)に戻ります。
1. `Import Project` を選択します。
![](https://files.catbox.moe/qckos0.png)
1. `Import Git Repository` を選択します。
![](https://files.catbox.moe/pqub9q.png)
1. root を選択して、すべてをそのままにしておき、PAT_1 という名前の環境変数を(下図のように)追加します。これには個人アクセストークン (PAT) が含まれており、[ここ](https://github.com/settings/tokens/new)で簡単に作成することができます (すべてをそのままにしておいて、何かに名前を付けてください。)
![](https://files.catbox.moe/0ez4g7.png)
1. デプロイをクリックすれば完了です。API を使用するためにあなたのドメインを参照してください!
</details>
## :sparkling_heart: Support the project
私はできる限りのことをオープンソースで行い、これらのプロジェクトを利用して支援を必要としている皆さんに返信するようにしています。もちろんです。
これは時間がかかります。無料でご利用いただけます。
しかし、もしあなたがこのプロジェクトを使っていて、それに満足しているのであれば、あるいは単に私にものを作り続けることを奨励したいのであれば、いくつかの方法があります。
- あなたの readme で github-readme-stats を使用して適切なクレジットを付与し、それにリンクします (´∀` )
- 主演とプロジェクトの共有 :rocket:
- [![paypal.me/anuraghazra](https://ionicabizau.github.io/badges/paypal.svg)](https://www.paypal.me/anuraghazra) - PayPal を介して 1 回限りの寄付を行うことができます。私はおそらく ~~コーヒー~~ お茶買うでしょう。 :tea:
Thanks! :heart:
---
Contributions を歓迎します! <3
このプロジェクトは :heart: と JavaScript で作られています。
+115
View File
@@ -0,0 +1,115 @@
const theme = require("../themes/index");
const fs = require("fs");
const TARGET_FILE = "./themes/README.md";
const REPO_CARD_LINKS_FLAG = "<!-- REPO_CARD_LINKS -->";
const STAT_CARD_LINKS_FLAG = "<!-- STATS_CARD_LINKS -->";
const STAT_CARD_TABLE_FLAG = "<!-- STATS_CARD_TABLE -->";
const REPO_CARD_TABLE_FLAG = "<!-- REPO_CARD_TABLE -->";
const THEME_TEMPLATE = `## Available Themes
<!-- DO NOT EDIT THIS FILE DIRECTLY -->
With inbuilt themes you can customize the look of the card without doing any manual customization.
Use \`?theme=THEME_NAME\` parameter like so :-
\`\`\`md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&theme=dark&show_icons=true)
\`\`\`
## Stats
> These themes work both for the Stats Card and Repo Card.
| | | |
| :--: | :--: | :--: |
${STAT_CARD_TABLE_FLAG}
## Repo Card
> These themes work both for the Stats Card and Repo Card.
| | | |
| :--: | :--: | :--: |
${REPO_CARD_TABLE_FLAG}
${STAT_CARD_LINKS_FLAG}
${REPO_CARD_LINKS_FLAG}
[add-theme]: https://github.com/anuraghazra/github-readme-stats/edit/master/themes/index.js
Wanted to add a new theme? Consider reading the [contribution guidelines](../CONTRIBUTING.md#themes-contribution) :D
`;
const createRepoMdLink = (theme) => {
return `\n[${theme}_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=${theme}`;
};
const createStatMdLink = (theme) => {
return `\n[${theme}]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=${theme}`;
};
const generateLinks = (fn) => {
return Object.keys(theme)
.map((name) => fn(name))
.join("");
};
const createTableItem = ({ link, label, isRepoCard }) => {
if (!link || !label) return "";
return `\`${label}\` ![${link}][${link}${isRepoCard ? "_repo" : ""}]`;
};
const generateTable = ({ isRepoCard }) => {
const rows = [];
const themes = Object.keys(theme).filter(
(name) => name !== (!isRepoCard ? "default_repocard" : "default")
);
for (let i = 0; i < themes.length; i += 3) {
const one = themes[i];
const two = themes[i + 1];
const three = themes[i + 2];
let tableItem1 = createTableItem({ link: one, label: one, isRepoCard });
let tableItem2 = createTableItem({ link: two, label: two, isRepoCard });
let tableItem3 = createTableItem({ link: three, label: three, isRepoCard });
if (three === undefined) {
tableItem3 = `[Add your theme][add-theme]`;
}
rows.push(`| ${tableItem1} | ${tableItem2} | ${tableItem3} |`);
// if it's the last row & the row has no empty space push a new row
if (three && i + 3 === themes.length) {
rows.push(`| [Add your theme][add-theme] | | |`);
}
}
return rows.join("\n");
};
const buildReadme = () => {
return THEME_TEMPLATE.split("\n")
.map((line) => {
if (line.includes(REPO_CARD_LINKS_FLAG)) {
return generateLinks(createRepoMdLink);
}
if (line.includes(STAT_CARD_LINKS_FLAG)) {
return generateLinks(createStatMdLink);
}
if (line.includes(REPO_CARD_TABLE_FLAG)) {
return generateTable({ isRepoCard: true });
}
if (line.includes(STAT_CARD_TABLE_FLAG)) {
return generateTable({ isRepoCard: false });
}
return line;
})
.join("\n");
};
fs.writeFileSync(TARGET_FILE, buildReadme());
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
set -x
set -e
export BRANCH_NAME=updated-theme-readme
git --version
git config --global user.email "no-reply@githubreadmestats.com"
git config --global user.name "Github Readme Stats Bot"
git branch -d $BRANCH_NAME || true
git checkout -b $BRANCH_NAME
git add --all
git commit --message "docs(theme): Auto update theme readme" || exit 0
git remote add origin-$BRANCH_NAME https://${PERSONAL_TOKEN}@github.com/${GH_REPO}.git
git push --force --quiet --set-upstream origin-$BRANCH_NAME $BRANCH_NAME
+93
View File
@@ -0,0 +1,93 @@
// https://stackoverflow.com/a/5263759/10629172
function normalcdf(mean, sigma, to) {
var z = (to - mean) / Math.sqrt(2 * sigma * sigma);
var t = 1 / (1 + 0.3275911 * Math.abs(z));
var a1 = 0.254829592;
var a2 = -0.284496736;
var a3 = 1.421413741;
var a4 = -1.453152027;
var a5 = 1.061405429;
var erf =
1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-z * z);
var sign = 1;
if (z < 0) {
sign = -1;
}
return (1 / 2) * (1 + sign * erf);
}
function calculateRank({
totalRepos,
totalCommits,
contributions,
followers,
prs,
issues,
stargazers,
}) {
const COMMITS_OFFSET = 1.65;
const CONTRIBS_OFFSET = 1.65;
const ISSUES_OFFSET = 1;
const STARS_OFFSET = 0.75;
const PRS_OFFSET = 0.5;
const FOLLOWERS_OFFSET = 0.45;
const REPO_OFFSET = 1;
const ALL_OFFSETS =
CONTRIBS_OFFSET +
ISSUES_OFFSET +
STARS_OFFSET +
PRS_OFFSET +
FOLLOWERS_OFFSET +
REPO_OFFSET;
const RANK_S_VALUE = 1;
const RANK_DOUBLE_A_VALUE = 25;
const RANK_A2_VALUE = 45;
const RANK_A3_VALUE = 60;
const RANK_B_VALUE = 100;
const TOTAL_VALUES =
RANK_S_VALUE + RANK_A2_VALUE + RANK_A3_VALUE + RANK_B_VALUE;
// prettier-ignore
const score = (
totalCommits * COMMITS_OFFSET +
contributions * CONTRIBS_OFFSET +
issues * ISSUES_OFFSET +
stargazers * STARS_OFFSET +
prs * PRS_OFFSET +
followers * FOLLOWERS_OFFSET +
totalRepos * REPO_OFFSET
) / 100;
const normalizedScore = normalcdf(score, TOTAL_VALUES, ALL_OFFSETS) * 100;
let level = "";
if (normalizedScore < RANK_S_VALUE) {
level = "S+";
}
if (
normalizedScore >= RANK_S_VALUE &&
normalizedScore < RANK_DOUBLE_A_VALUE
) {
level = "S";
}
if (
normalizedScore >= RANK_DOUBLE_A_VALUE &&
normalizedScore < RANK_A2_VALUE
) {
level = "A++";
}
if (normalizedScore >= RANK_A2_VALUE && normalizedScore < RANK_A3_VALUE) {
level = "A+";
}
if (normalizedScore >= RANK_A3_VALUE && normalizedScore < RANK_B_VALUE) {
level = "B+";
}
return { level, score: normalizedScore };
}
module.exports = calculateRank;
+80
View File
@@ -0,0 +1,80 @@
const { request } = require("./utils");
const retryer = require("./retryer");
const fetcher = (variables, token) => {
return request(
{
query: `
fragment RepoInfo on Repository {
name
nameWithOwner
isPrivate
isArchived
isTemplate
stargazers {
totalCount
}
description
primaryLanguage {
color
id
name
}
forkCount
}
query getRepo($login: String!, $repo: String!) {
user(login: $login) {
repository(name: $repo) {
...RepoInfo
}
}
organization(login: $login) {
repository(name: $repo) {
...RepoInfo
}
}
}
`,
variables,
},
{
Authorization: `bearer ${token}`,
}
);
};
async function fetchRepo(username, reponame) {
if (!username || !reponame) {
throw new Error("Invalid username or reponame");
}
let res = await retryer(fetcher, { login: username, repo: reponame });
const data = res.data.data;
if (!data.user && !data.organization) {
throw new Error("Not found");
}
const isUser = data.organization === null && data.user;
const isOrg = data.user === null && data.organization;
if (isUser) {
if (!data.user.repository || data.user.repository.isPrivate) {
throw new Error("User Repository Not found");
}
return data.user.repository;
}
if (isOrg) {
if (
!data.organization.repository ||
data.organization.repository.isPrivate
) {
throw new Error("Organization Repository Not found");
}
return data.organization.repository;
}
}
module.exports = fetchRepo;
+102
View File
@@ -0,0 +1,102 @@
const { request, logger } = require("./utils");
const retryer = require("./retryer");
const calculateRank = require("./calculateRank");
require("dotenv").config();
const fetcher = (variables, token) => {
return request(
{
query: `
query userInfo($login: String!) {
user(login: $login) {
name
login
contributionsCollection {
totalCommitContributions
restrictedContributionsCount
}
repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount
}
pullRequests(first: 1) {
totalCount
}
issues(first: 1) {
totalCount
}
followers {
totalCount
}
repositories(first: 100, ownerAffiliations: OWNER, isFork: false, orderBy: {direction: DESC, field: STARGAZERS}) {
totalCount
nodes {
stargazers {
totalCount
}
}
}
}
}
`,
variables,
},
{
Authorization: `bearer ${token}`,
}
);
};
async function fetchStats(username, count_private = false) {
if (!username) throw Error("Invalid username");
const stats = {
name: "",
totalPRs: 0,
totalCommits: 0,
totalIssues: 0,
totalStars: 0,
contributedTo: 0,
rank: { level: "C", score: 0 },
};
let res = await retryer(fetcher, { login: username });
if (res.data.errors) {
logger.error(res.data.errors);
throw Error(res.data.errors[0].message || "Could not fetch user");
}
const user = res.data.data.user;
const contributionCount = user.contributionsCollection;
stats.name = user.name || user.login;
stats.totalIssues = user.issues.totalCount;
stats.totalCommits = contributionCount.totalCommitContributions;
if (count_private) {
stats.totalCommits =
contributionCount.totalCommitContributions +
contributionCount.restrictedContributionsCount;
}
stats.totalPRs = user.pullRequests.totalCount;
stats.contributedTo = user.repositoriesContributedTo.totalCount;
stats.totalStars = user.repositories.nodes.reduce((prev, curr) => {
return prev + curr.stargazers.totalCount;
}, 0);
stats.rank = calculateRank({
totalCommits: stats.totalCommits,
totalRepos: user.repositories.totalCount,
followers: user.followers.totalCount,
contributions: stats.contributedTo,
stargazers: stats.totalStars,
prs: stats.totalPRs,
issues: stats.totalIssues,
});
return stats;
}
module.exports = fetchStats;
+85
View File
@@ -0,0 +1,85 @@
const { request, logger } = require("./utils");
const retryer = require("./retryer");
require("dotenv").config();
const fetcher = (variables, token) => {
return request(
{
query: `
query userInfo($login: String!) {
user(login: $login) {
# fetch only owner repos & not forks
repositories(ownerAffiliations: OWNER, isFork: false, first: 100) {
nodes {
languages(first: 10, orderBy: {field: SIZE, direction: DESC}) {
edges {
size
node {
color
name
}
}
}
}
}
}
}
`,
variables,
},
{
Authorization: `bearer ${token}`,
}
);
};
async function fetchTopLanguages(username) {
if (!username) throw Error("Invalid username");
let res = await retryer(fetcher, { login: username });
if (res.data.errors) {
logger.error(res.data.errors);
throw Error(res.data.errors[0].message || "Could not fetch user");
}
let repoNodes = res.data.data.user.repositories.nodes;
repoNodes = repoNodes
.filter((node) => {
return node.languages.edges.length > 0;
})
// flatten the list of language nodes
.reduce((acc, curr) => curr.languages.edges.concat(acc), [])
.sort((a, b) => b.size - a.size)
.reduce((acc, prev) => {
// get the size of the language (bytes)
let langSize = prev.size;
// if we already have the language in the accumulator
// & the current language name is same as previous name
// add the size to the language size.
if (acc[prev.node.name] && prev.node.name === acc[prev.node.name].name) {
langSize = prev.size + acc[prev.node.name].size;
}
return {
...acc,
[prev.node.name]: {
name: prev.node.name,
color: prev.node.color,
size: langSize,
},
};
}, {});
const topLangs = Object.keys(repoNodes)
.slice(0, 5)
.reduce((result, key) => {
result[key] = repoNodes[key];
return result;
}, {});
return topLangs;
}
module.exports = fetchTopLanguages;
+94
View File
@@ -0,0 +1,94 @@
const calculateCircleProgress = (value) => {
let radius = 40;
let c = Math.PI * (radius * 2);
if (value < 0) value = 0;
if (value > 100) value = 100;
let percentage = ((100 - value) / 100) * c;
return percentage;
};
const getAnimations = ({ progress }) => {
return `
/* Animations */
@keyframes scaleIn {
from {
transform: translate(-5px, 5px) scale(0);
}
to {
transform: translate(-5px, 5px) scale(1);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes rankAnimation {
from {
stroke-dashoffset: ${calculateCircleProgress(0)};
}
to {
stroke-dashoffset: ${calculateCircleProgress(progress)};
}
}
`;
};
const getStyles = ({
titleColor,
textColor,
iconColor,
show_icons,
progress,
}) => {
return `
.header {
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${titleColor};
animation: fadeIn 0.8s ease-in-out forwards;
}
.stat {
font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${textColor};
}
.stagger {
opacity: 0;
animation: fadeIn 0.3s ease-in-out forwards;
}
.rank-text {
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor};
animation: scaleIn 0.3s ease-in-out forwards;
}
.bold { font-weight: 700 }
.icon {
fill: ${iconColor};
display: ${!!show_icons ? "block" : "none"};
}
.rank-circle-rim {
stroke: ${titleColor};
fill: none;
stroke-width: 6;
opacity: 0.2;
}
.rank-circle {
stroke: ${titleColor};
stroke-dasharray: 250;
fill: none;
stroke-width: 6;
stroke-linecap: round;
opacity: 0.8;
transform-origin: -10px 8px;
transform: rotate(-90deg);
animation: rankAnimation 1s forwards ease-in-out;
}
${process.env.NODE_ENV === "test" ? "" : getAnimations({ progress })}
`;
};
module.exports = getStyles;
+11
View File
@@ -0,0 +1,11 @@
const icons = {
star: `<path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"/>`,
commits: `<path fill-rule="evenodd" d="M1.643 3.143L.427 1.927A.25.25 0 000 2.104V5.75c0 .138.112.25.25.25h3.646a.25.25 0 00.177-.427L2.715 4.215a6.5 6.5 0 11-1.18 4.458.75.75 0 10-1.493.154 8.001 8.001 0 101.6-5.684zM7.75 4a.75.75 0 01.75.75v2.992l2.028.812a.75.75 0 01-.557 1.392l-2.5-1A.75.75 0 017 8.25v-3.5A.75.75 0 017.75 4z"/>`,
prs: `<path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"/>`,
issues: `<path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"/>`,
icon: `<path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"/>`,
contribs: `<path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"/>`,
fork: `<path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>`,
};
module.exports = icons;
+151
View File
@@ -0,0 +1,151 @@
const {
kFormatter,
encodeHTML,
getCardColors,
FlexLayout,
wrapTextMultiline,
} = require("../src/utils");
const icons = require("./icons");
const toEmoji = require("emoji-name-map");
const renderRepoCard = (repo, options = {}) => {
const {
name,
nameWithOwner,
description,
primaryLanguage,
stargazers,
isArchived,
isTemplate,
forkCount,
} = repo;
const {
title_color,
icon_color,
text_color,
bg_color,
show_owner,
theme = "default_repocard",
} = options;
const header = show_owner ? nameWithOwner : name;
const langName = (primaryLanguage && primaryLanguage.name) || "Unspecified";
const langColor = (primaryLanguage && primaryLanguage.color) || "#333";
const shiftText = langName.length > 15 ? 0 : 30;
let desc = description || "No description provided";
// parse emojis to unicode
desc = desc.replace(/:\w+:/gm, (emoji) => {
return toEmoji.get(emoji) || "";
});
const multiLineDescription = wrapTextMultiline(desc);
const descriptionLines = multiLineDescription.length;
const lineHeight = 10;
const height =
(descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight;
// returns theme based colors with proper overrides and defaults
const { titleColor, textColor, iconColor, bgColor } = getCardColors({
title_color,
icon_color,
text_color,
bg_color,
theme,
});
const totalStars = kFormatter(stargazers.totalCount);
const totalForks = kFormatter(forkCount);
const getBadgeSVG = (label) => `
<g data-testid="badge" class="badge" transform="translate(320, 38)">
<rect stroke="${textColor}" stroke-width="1" width="70" height="20" x="-12" y="-14" ry="10" rx="10"></rect>
<text
x="23" y="-5"
alignment-baseline="central"
dominant-baseline="central"
text-anchor="middle"
fill="${textColor}"
>
${label}
</text>
</g>
`;
const svgLanguage = primaryLanguage
? `
<g data-testid="primary-lang" transform="translate(30, 0)">
<circle data-testid="lang-color" cx="0" cy="-5" r="6" fill="${langColor}" />
<text data-testid="lang-name" class="gray" x="15">${langName}</text>
</g>
`
: "";
const svgStars =
stargazers.totalCount > 0 &&
`
<svg class="icon" y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16">
${icons.star}
</svg>
<text data-testid="stargazers" class="gray" x="25">${totalStars}</text>
`;
const svgForks =
forkCount > 0 &&
`
<svg class="icon" y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16">
${icons.fork}
</svg>
<text data-testid="forkcount" class="gray" x="25">${totalForks}</text>
`;
return `
<svg version="1.1" width="400" height="${height}" viewBox="0 0 400 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${titleColor} }
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
.icon { fill: ${iconColor} }
.badge { font: 600 11px 'Segoe UI', Ubuntu, Sans-Serif; }
.badge rect { opacity: 0.2 }
</style>
<rect data-testid="card-bg" x="0.5" y="0.5" width="399" height="99%" rx="4.5" fill="${bgColor}" stroke="#E4E2E2"/>
<svg class="icon" x="25" y="25" viewBox="0 0 16 16" version="1.1" width="16" height="16">
${icons.contribs}
</svg>
<text x="50" y="38" class="header">${header}</text>
${
isTemplate
? getBadgeSVG("Template")
: isArchived
? getBadgeSVG("Archived")
: ""
}
<text class="description" x="25" y="50">
${multiLineDescription
.map((line) => `<tspan dy="1.2em" x="25">${encodeHTML(line)}</tspan>`)
.join("")}
</text>
<g transform="translate(0, ${height - 20})">
${svgLanguage}
<g
data-testid="star-fork-group"
transform="translate(${primaryLanguage ? 155 - shiftText : 25}, 0)"
>
${FlexLayout({ items: [svgStars, svgForks], gap: 65 }).join("")}
</g>
</g>
</svg>
`;
};
module.exports = renderRepoCard;
+202
View File
@@ -0,0 +1,202 @@
const {
kFormatter,
getCardColors,
FlexLayout,
encodeHTML,
} = require("../src/utils");
const getStyles = require("./getStyles");
const icons = require("./icons");
const createTextNode = ({ icon, label, value, id, index, showIcons }) => {
const kValue = kFormatter(value);
const staggerDelay = (index + 3) * 150;
const labelOffset = showIcons ? `x="25"` : "";
const iconSvg = showIcons
? `
<svg data-testid="icon" class="icon" viewBox="0 0 16 16" version="1.1" width="16" height="16">
${icon}
</svg>
`
: "";
return `
<g class="stagger" style="animation-delay: ${staggerDelay}ms" transform="translate(25, 0)">
${iconSvg}
<text class="stat bold" ${labelOffset} y="12.5">${label}:</text>
<text class="stat" x="135" y="12.5" data-testid="${id}">${kValue}</text>
</g>
`;
};
const renderStatsCard = (stats = {}, options = { hide: [] }) => {
const {
name,
totalStars,
totalCommits,
totalIssues,
totalPRs,
contributedTo,
rank,
} = stats;
const {
hide = [],
show_icons = false,
hide_title = false,
hide_border = false,
hide_rank = false,
line_height = 25,
title_color,
icon_color,
text_color,
bg_color,
theme = "default",
} = options;
const lheight = parseInt(line_height);
// returns theme based colors with proper overrides and defaults
const { titleColor, textColor, iconColor, bgColor } = getCardColors({
title_color,
icon_color,
text_color,
bg_color,
theme,
});
// Meta data for creating text nodes with createTextNode function
const STATS = {
stars: {
icon: icons.star,
label: "Total Stars",
value: totalStars,
id: "stars",
},
commits: {
icon: icons.commits,
label: "Total Commits",
value: totalCommits,
id: "commits",
},
prs: {
icon: icons.prs,
label: "Total PRs",
value: totalPRs,
id: "prs",
},
issues: {
icon: icons.issues,
label: "Total Issues",
value: totalIssues,
id: "issues",
},
contribs: {
icon: icons.contribs,
label: "Contributed to",
value: contributedTo,
id: "contribs",
},
};
// filter out hidden stats defined by user & create the text nodes
const statItems = Object.keys(STATS)
.filter((key) => !hide.includes(key))
.map((key, index) =>
// create the text nodes, and pass index so that we can calculate the line spacing
createTextNode({
...STATS[key],
index,
showIcons: show_icons,
})
);
// Calculate the card height depending on how many items there are
// but if rank circle is visible clamp the minimum height to `150`
let height = Math.max(
45 + (statItems.length + 1) * lheight,
hide_rank ? 0 : 150
);
// the better user's score the the rank will be closer to zero so
// subtracting 100 to get the progress in 100%
const progress = 100 - rank.score;
const styles = getStyles({
titleColor,
textColor,
iconColor,
show_icons,
progress,
});
// Conditionally rendered elements
const apostrophe = ["x", "s"].includes(name.slice(-1)) ? "" : "s";
const title = hide_title
? ""
: `<text x="25" y="35" class="header">${encodeHTML(name)}'${apostrophe} GitHub Stats</text>`;
const border = `
<rect
data-testid="card-bg"
x="0.5"
y="0.5"
width="494"
height="99%"
rx="4.5"
fill="${bgColor}"
stroke="#E4E2E2"
stroke-opacity="${hide_border ? 0 : 1}"
/>
`;
const rankCircle = hide_rank
? ""
: `<g data-testid="rank-circle" transform="translate(400, ${
height / 1.85
})">
<circle class="rank-circle-rim" cx="-10" cy="8" r="40" />
<circle class="rank-circle" cx="-10" cy="8" r="40" />
<g class="rank-text">
<text
x="${rank.level.length === 1 ? "-4" : "0"}"
y="0"
alignment-baseline="central"
dominant-baseline="central"
text-anchor="middle"
>
${rank.level}
</text>
</g>
</g>`;
if (hide_title) {
height -= 30;
}
return `
<svg width="495" height="${height}" viewBox="0 0 495 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
${styles}
</style>
${border}
${title}
<g data-testid="card-body-content" transform="translate(0, ${
hide_title ? -30 : 0
})">
${rankCircle}
<svg x="0" y="55">
${FlexLayout({
items: statItems,
gap: lheight,
direction: "column",
}).join("")}
</svg>
</g>
</svg>
`;
};
module.exports = renderStatsCard;
+198
View File
@@ -0,0 +1,198 @@
const { getCardColors, FlexLayout, clampValue } = require("../src/utils");
const createProgressNode = ({ width, color, name, progress }) => {
const paddingRight = 95;
const progressTextX = width - paddingRight + 10;
const progressWidth = width - paddingRight;
const progressPercentage = clampValue(progress, 2, 100);
return `
<text data-testid="lang-name" x="2" y="15" class="lang-name">${name}</text>
<text x="${progressTextX}" y="34" class="lang-name">${progress}%</text>
<svg width="${progressWidth}">
<rect rx="5" ry="5" x="0" y="25" width="${progressWidth}" height="8" fill="#ddd"></rect>
<rect
height="8"
fill="${color}"
rx="5" ry="5" x="0" y="25"
data-testid="lang-progress"
width="${progressPercentage}%"
>
</rect>
</svg>
`;
};
const createCompactLangNode = ({ lang, totalSize, x, y }) => {
const percentage = ((lang.size / totalSize) * 100).toFixed(2);
const color = lang.color || "#858585";
return `
<g transform="translate(${x}, ${y})">
<circle cx="5" cy="6" r="5" fill="${color}" />
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
${lang.name} ${percentage}%
</text>
</g>
`;
};
const createLanguageTextNode = ({ langs, totalSize, x, y }) => {
return langs.map((lang, index) => {
if (index % 2 === 0) {
return createCompactLangNode({
lang,
x,
y: 12.5 * index + y,
totalSize,
index,
});
}
return createCompactLangNode({
lang,
x: 150,
y: 12.5 + 12.5 * index,
totalSize,
index,
});
});
};
const lowercaseTrim = (name) => name.toLowerCase().trim();
const renderTopLanguages = (topLangs, options = {}) => {
const {
hide_title,
card_width,
title_color,
text_color,
bg_color,
hide,
theme,
layout,
} = options;
let langs = Object.values(topLangs);
let langsToHide = {};
// populate langsToHide map for quick lookup
// while filtering out
if (hide) {
hide.forEach((langName) => {
langsToHide[lowercaseTrim(langName)] = true;
});
}
// filter out langauges to be hidden
langs = langs
.sort((a, b) => b.size - a.size)
.filter((lang) => {
return !langsToHide[lowercaseTrim(lang.name)];
});
const totalLanguageSize = langs.reduce((acc, curr) => {
return acc + curr.size;
}, 0);
// returns theme based colors with proper overrides and defaults
const { titleColor, textColor, bgColor } = getCardColors({
title_color,
text_color,
bg_color,
theme,
});
let width = isNaN(card_width) ? 300 : card_width;
let height = 45 + (langs.length + 1) * 40;
let finalLayout = "";
// RENDER COMPACT LAYOUT
if (layout === "compact") {
width = width + 50;
height = 30 + (langs.length / 2 + 1) * 40;
// progressOffset holds the previous language's width and used to offset the next language
// so that we can stack them one after another, like this: [--][----][---]
let progressOffset = 0;
const compactProgressBar = langs
.map((lang) => {
const percentage = (
(lang.size / totalLanguageSize) *
(width - 50)
).toFixed(2);
const progress =
percentage < 10 ? parseFloat(percentage) + 10 : percentage;
const output = `
<rect
mask="url(#rect-mask)"
data-testid="lang-progress"
x="${progressOffset}"
y="0"
width="${progress}"
height="8"
fill="${lang.color || "#858585"}"
/>
`;
progressOffset += parseFloat(percentage);
return output;
})
.join("");
finalLayout = `
<mask id="rect-mask">
<rect x="0" y="0" width="${
width - 50
}" height="8" fill="white" rx="5" />
</mask>
${compactProgressBar}
${createLanguageTextNode({
x: 0,
y: 25,
langs,
totalSize: totalLanguageSize,
}).join("")}
`;
} else {
finalLayout = FlexLayout({
items: langs.map((lang) => {
return createProgressNode({
width: width,
name: lang.name,
color: lang.color || "#858585",
progress: ((lang.size / totalLanguageSize) * 100).toFixed(2),
});
}),
gap: 40,
direction: "column",
}).join("");
}
if (hide_title) {
height -= 30;
}
return `
<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${titleColor} }
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
</style>
<rect data-testid="card-bg" x="0.5" y="0.5" width="99.7%" height="99%" rx="4.5" fill="${bgColor}" stroke="#E4E2E2"/>
${
hide_title
? ""
: `<text data-testid="header" x="25" y="35" class="header">Most Used Languages</text>`
}
<svg data-testid="lang-items" x="25" y="${hide_title ? 25 : 55}">
${finalLayout}
</svg>
</svg>
`;
};
module.exports = renderTopLanguages;
+45
View File
@@ -0,0 +1,45 @@
const { logger } = require("./utils");
const retryer = async (fetcher, variables, retries = 0) => {
if (retries > 7) {
throw new Error("Maximum retries exceeded");
}
try {
logger.log(`Trying PAT_${retries + 1}`);
// try to fetch with the first token since RETRIES is 0 index i'm adding +1
let response = await fetcher(
variables,
process.env[`PAT_${retries + 1}`],
retries
);
// prettier-ignore
const isRateExceeded = response.data.errors && response.data.errors[0].type === "RATE_LIMITED";
// if rate limit is hit increase the RETRIES and recursively call the retryer
// with username, and current RETRIES
if (isRateExceeded) {
logger.log(`PAT_${retries + 1} Failed`);
retries++;
// directly return from the function
return retryer(fetcher, variables, retries);
}
// finally return the response
return response;
} catch (err) {
// prettier-ignore
// also checking for bad credentials if any tokens gets invalidated
const isBadCredential = err.response.data && err.response.data.message === "Bad credentials";
if (isBadCredential) {
logger.log(`PAT_${retries + 1} Failed`);
retries++;
// directly return from the function
return retryer(fetcher, variables, retries);
}
}
};
module.exports = retryer;
+174
View File
@@ -0,0 +1,174 @@
const axios = require("axios");
const wrap = require("word-wrap");
const themes = require("../themes");
const renderError = (message, secondaryMessage = "") => {
return `
<svg width="495" height="120" viewBox="0 0 495 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.text { font: 600 16px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
.small { font: 600 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #252525 }
.gray { fill: #858585 }
</style>
<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
<text x="25" y="45" class="text">Something went wrong! file an issue at https://git.io/JJmN9</text>
<text data-testid="message" x="25" y="55" class="text small">
<tspan x="25" dy="18">${message}</tspan>
<tspan x="25" dy="18" class="gray">${secondaryMessage}</tspan>
</text>
</svg>
`;
};
// https://stackoverflow.com/a/48073476/10629172
function encodeHTML(str) {
return str
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
return "&#" + i.charCodeAt(0) + ";";
})
.replace(/\u0008/gim, "");
}
function kFormatter(num) {
return Math.abs(num) > 999
? Math.sign(num) * (Math.abs(num) / 1000).toFixed(1) + "k"
: Math.sign(num) * Math.abs(num);
}
function isValidHexColor(hexColor) {
return new RegExp(
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/
).test(hexColor);
}
function parseBoolean(value) {
if (value === "true") {
return true;
} else if (value === "false") {
return false;
} else {
return value;
}
}
function parseArray(str) {
if (!str) return [];
return str.split(",");
}
function clampValue(number, min, max) {
return Math.max(min, Math.min(number, max));
}
function fallbackColor(color, fallbackColor) {
return (isValidHexColor(color) && `#${color}`) || fallbackColor;
}
function request(data, headers) {
return axios({
url: "https://api.github.com/graphql",
method: "post",
headers,
data,
});
}
/**
*
* @param {String[]} items
* @param {Number} gap
* @param {string} direction
*
* @description
* Auto layout utility, allows us to layout things
* vertically or horizontally with proper gaping
*/
function FlexLayout({ items, gap, direction }) {
// filter() for filtering out empty strings
return items.filter(Boolean).map((item, i) => {
let transform = `translate(${gap * i}, 0)`;
if (direction === "column") {
transform = `translate(0, ${gap * i})`;
}
return `<g transform="${transform}">${item}</g>`;
});
}
// returns theme based colors with proper overrides and defaults
function getCardColors({
title_color,
text_color,
icon_color,
bg_color,
theme,
fallbackTheme = "default",
}) {
const defaultTheme = themes[fallbackTheme];
const selectedTheme = themes[theme] || defaultTheme;
// get the color provided by the user else the theme color
// finally if both colors are invalid fallback to default theme
const titleColor = fallbackColor(
title_color || selectedTheme.title_color,
"#" + defaultTheme.title_color
);
const iconColor = fallbackColor(
icon_color || selectedTheme.icon_color,
"#" + defaultTheme.icon_color
);
const textColor = fallbackColor(
text_color || selectedTheme.text_color,
"#" + defaultTheme.text_color
);
const bgColor = fallbackColor(
bg_color || selectedTheme.bg_color,
"#" + defaultTheme.bg_color
);
return { titleColor, iconColor, textColor, bgColor };
}
function wrapTextMultiline(text, width = 60, maxLines = 3) {
const wrapped = wrap(encodeHTML(text), { width })
.split("\n") // Split wrapped lines to get an array of lines
.map((line) => line.trim()); // Remove leading and trailing whitespace of each line
const lines = wrapped.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 =
process.env.NODE_ENV !== "test" ? console : { log: noop, error: noop };
const CONSTANTS = {
THIRTY_MINUTES: 1800,
TWO_HOURS: 7200,
ONE_DAY: 86400,
};
module.exports = {
renderError,
kFormatter,
encodeHTML,
isValidHexColor,
request,
parseArray,
parseBoolean,
fallbackColor,
FlexLayout,
getCardColors,
clampValue,
wrapTextMultiline,
logger,
CONSTANTS,
};
+224
View File
@@ -0,0 +1,224 @@
require("@testing-library/jest-dom");
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const api = require("../api/index");
const renderStatsCard = require("../src/renderStatsCard");
const { renderError, CONSTANTS } = require("../src/utils");
const calculateRank = require("../src/calculateRank");
const stats = {
name: "Anurag Hazra",
totalStars: 100,
totalCommits: 200,
totalIssues: 300,
totalPRs: 400,
contributedTo: 500,
rank: null,
};
stats.rank = calculateRank({
totalCommits: stats.totalCommits,
totalRepos: 1,
followers: 0,
contributions: stats.contributedTo,
stargazers: stats.totalStars,
prs: stats.totalPRs,
issues: stats.totalIssues,
});
const data = {
data: {
user: {
name: stats.name,
repositoriesContributedTo: { totalCount: stats.contributedTo },
contributionsCollection: {
totalCommitContributions: stats.totalCommits,
restrictedContributionsCount: 100,
},
pullRequests: { totalCount: stats.totalPRs },
issues: { totalCount: stats.totalIssues },
followers: { totalCount: 0 },
repositories: {
totalCount: 1,
nodes: [{ stargazers: { totalCount: 100 } }],
},
},
},
};
const error = {
errors: [
{
type: "NOT_FOUND",
path: ["user"],
locations: [],
message: "Could not fetch user",
},
],
};
const mock = new MockAdapter(axios);
const faker = (query, data) => {
const req = {
query: {
username: "anuraghazra",
...query,
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data);
return { req, res };
};
afterEach(() => {
mock.reset();
});
describe("Test /api/", () => {
it("should test the request", async () => {
const { req, res } = faker({}, data);
await api(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(renderStatsCard(stats, { ...req.query }));
});
it("should render error card on error", async () => {
const { req, res } = faker({}, error);
await api(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(
renderError(
error.errors[0].message,
"Make sure the provided username is not an organization"
)
);
});
it("should get the query options", async () => {
const { req, res } = faker(
{
username: "anuraghazra",
hide: "issues,prs,contribs",
show_icons: true,
hide_border: true,
line_height: 100,
title_color: "fff",
icon_color: "fff",
text_color: "fff",
bg_color: "fff",
},
data
);
await api(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(
renderStatsCard(stats, {
hide: ["issues", "prs", "contribs"],
show_icons: true,
hide_border: true,
line_height: 100,
title_color: "fff",
icon_color: "fff",
text_color: "fff",
bg_color: "fff",
})
);
});
it("should have proper cache", async () => {
const { req, res } = faker({}, data);
mock.onPost("https://api.github.com/graphql").reply(200, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.THIRTY_MINUTES}`],
]);
});
it("should set proper cache", async () => {
const { req, res } = faker({ cache_seconds: 2000 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${2000}`],
]);
});
it("should set proper cache with clamped values", async () => {
{
let { req, res } = faker({ cache_seconds: 200000 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.ONE_DAY}`],
]);
}
// note i'm using block scoped vars
{
let { req, res } = faker({ cache_seconds: 0 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.THIRTY_MINUTES}`],
]);
}
{
let { req, res } = faker({ cache_seconds: -10000 }, data);
await api(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `public, max-age=${CONSTANTS.THIRTY_MINUTES}`],
]);
}
});
it("should add private contributions", async () => {
const { req, res } = faker(
{
username: "anuraghazra",
count_private: true,
},
data
);
await api(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(
renderStatsCard(
{
...stats,
totalCommits: stats.totalCommits + 100,
rank: calculateRank({
totalCommits: stats.totalCommits + 100,
totalRepos: 1,
followers: 0,
contributions: stats.contributedTo,
stargazers: stats.totalStars,
prs: stats.totalPRs,
issues: stats.totalIssues,
}),
},
{}
)
);
});
});
+18
View File
@@ -0,0 +1,18 @@
require("@testing-library/jest-dom");
const calculateRank = require("../src/calculateRank");
describe("Test calculateRank", () => {
it("should calculate rank correctly", () => {
expect(
calculateRank({
totalCommits: 100,
totalRepos: 5,
followers: 100,
contributions: 61,
stargazers: 400,
prs: 300,
issues: 200,
})
).toStrictEqual({ level: "A+", score: 49.16605417270399 });
});
});
+96
View File
@@ -0,0 +1,96 @@
require("@testing-library/jest-dom");
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const fetchRepo = require("../src/fetchRepo");
const data_repo = {
repository: {
name: "convoychat",
stargazers: { totalCount: 38000 },
description: "Help us take over the world! React + TS + GraphQL Chat App",
primaryLanguage: {
color: "#2b7489",
id: "MDg6TGFuZ3VhZ2UyODc=",
name: "TypeScript",
},
forkCount: 100,
},
};
const data_user = {
data: {
user: { repository: data_repo },
organization: null,
},
};
const data_org = {
data: {
user: null,
organization: { repository: data_repo },
},
};
const mock = new MockAdapter(axios);
afterEach(() => {
mock.reset();
});
describe("Test fetchRepo", () => {
it("should fetch correct user repo", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data_user);
let repo = await fetchRepo("anuraghazra", "convoychat");
expect(repo).toStrictEqual(data_repo);
});
it("should fetch correct org repo", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data_org);
let repo = await fetchRepo("anuraghazra", "convoychat");
expect(repo).toStrictEqual(data_repo);
});
it("should throw error if user is found but repo is null", async () => {
mock
.onPost("https://api.github.com/graphql")
.reply(200, { data: { user: { repository: null }, organization: null } });
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
"User Repository Not found"
);
});
it("should throw error if org is found but repo is null", async () => {
mock
.onPost("https://api.github.com/graphql")
.reply(200, { data: { user: null, organization: { repository: null } } });
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
"Organization Repository Not found"
);
});
it("should throw error if both user & org data not found", async () => {
mock
.onPost("https://api.github.com/graphql")
.reply(200, { data: { user: null, organization: null } });
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
"Not found"
);
});
it("should throw error if repository is private", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, {
data: {
user: { repository: { ...data_repo, isPrivate: true } },
organization: null,
},
});
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
"User Repository Not found"
);
});
});
+105
View File
@@ -0,0 +1,105 @@
require("@testing-library/jest-dom");
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const fetchStats = require("../src/fetchStats");
const calculateRank = require("../src/calculateRank");
const data = {
data: {
user: {
name: "Anurag Hazra",
repositoriesContributedTo: { totalCount: 61 },
contributionsCollection: { totalCommitContributions: 100, restrictedContributionsCount: 50 },
pullRequests: { totalCount: 300 },
issues: { totalCount: 200 },
followers: { totalCount: 100 },
repositories: {
totalCount: 5,
nodes: [
{ stargazers: { totalCount: 100 } },
{ stargazers: { totalCount: 100 } },
{ stargazers: { totalCount: 100 } },
{ stargazers: { totalCount: 50 } },
{ stargazers: { totalCount: 50 } },
],
},
},
},
};
const error = {
errors: [
{
type: "NOT_FOUND",
path: ["user"],
locations: [],
message: "Could not resolve to a User with the login of 'noname'.",
},
],
};
const mock = new MockAdapter(axios);
afterEach(() => {
mock.reset();
});
describe("Test fetchStats", () => {
it("should fetch correct stats", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data);
let stats = await fetchStats("anuraghazra");
const rank = calculateRank({
totalCommits: 100,
totalRepos: 5,
followers: 100,
contributions: 61,
stargazers: 400,
prs: 300,
issues: 200,
});
expect(stats).toStrictEqual({
contributedTo: 61,
name: "Anurag Hazra",
totalCommits: 100,
totalIssues: 200,
totalPRs: 300,
totalStars: 400,
rank,
});
});
it("should throw error", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, error);
await expect(fetchStats("anuraghazra")).rejects.toThrow(
"Could not resolve to a User with the login of 'noname'."
);
});
it("should fetch and add private contributions", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data);
let stats = await fetchStats("anuraghazra", true);
const rank = calculateRank({
totalCommits: 150,
totalRepos: 5,
followers: 100,
contributions: 61,
stargazers: 400,
prs: 300,
issues: 200,
});
expect(stats).toStrictEqual({
contributedTo: 61,
name: "Anurag Hazra",
totalCommits: 150,
totalIssues: 200,
totalPRs: 300,
totalStars: 400,
rank,
});
});
});
+84
View File
@@ -0,0 +1,84 @@
require("@testing-library/jest-dom");
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const fetchTopLanguages = require("../src/fetchTopLanguages");
const mock = new MockAdapter(axios);
afterEach(() => {
mock.reset();
});
const data_langs = {
data: {
user: {
repositories: {
nodes: [
{
languages: {
edges: [{ size: 100, node: { color: "#0f0", name: "HTML" } }],
},
},
{
languages: {
edges: [{ size: 100, node: { color: "#0f0", name: "HTML" } }],
},
},
{
languages: {
edges: [
{ size: 100, node: { color: "#0ff", name: "javascript" } },
],
},
},
{
languages: {
edges: [
{ size: 100, node: { color: "#0ff", name: "javascript" } },
],
},
},
],
},
},
},
};
const error = {
errors: [
{
type: "NOT_FOUND",
path: ["user"],
locations: [],
message: "Could not resolve to a User with the login of 'noname'.",
},
],
};
describe("FetchTopLanguages", () => {
it("should fetch correct language data", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data_langs);
let repo = await fetchTopLanguages("anuraghazra");
expect(repo).toStrictEqual({
HTML: {
color: "#0f0",
name: "HTML",
size: 200,
},
javascript: {
color: "#0ff",
name: "javascript",
size: 200,
},
});
});
it("should throw error", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, error);
await expect(fetchTopLanguages("anuraghazra")).rejects.toThrow(
"Could not resolve to a User with the login of 'noname'."
);
});
});
+126
View File
@@ -0,0 +1,126 @@
require("@testing-library/jest-dom");
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const pin = require("../api/pin");
const renderRepoCard = require("../src/renderRepoCard");
const { renderError } = require("../src/utils");
const data_repo = {
repository: {
username: "anuraghazra",
name: "convoychat",
stargazers: { totalCount: 38000 },
description: "Help us take over the world! React + TS + GraphQL Chat App",
primaryLanguage: {
color: "#2b7489",
id: "MDg6TGFuZ3VhZ2UyODc=",
name: "TypeScript",
},
forkCount: 100,
isTemplate: false
},
};
const data_user = {
data: {
user: { repository: data_repo.repository },
organization: null,
},
};
const mock = new MockAdapter(axios);
afterEach(() => {
mock.reset();
});
describe("Test /api/pin", () => {
it("should test the request", async () => {
const req = {
query: {
username: "anuraghazra",
repo: "convoychat",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data_user);
await pin(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(renderRepoCard(data_repo.repository));
});
it("should get the query options", async () => {
const req = {
query: {
username: "anuraghazra",
repo: "convoychat",
title_color: "fff",
icon_color: "fff",
text_color: "fff",
bg_color: "fff",
full_name: "1",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data_user);
await pin(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(
renderRepoCard(data_repo.repository, { ...req.query })
);
});
it("should render error card if user repo not found", async () => {
const req = {
query: {
username: "anuraghazra",
repo: "convoychat",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock
.onPost("https://api.github.com/graphql")
.reply(200, { data: { user: { repository: null }, organization: null } });
await pin(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(renderError("User Repository Not found"));
});
it("should render error card if org repo not found", async () => {
const req = {
query: {
username: "anuraghazra",
repo: "convoychat",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock
.onPost("https://api.github.com/graphql")
.reply(200, { data: { user: null, organization: { repository: null } } });
await pin(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(
renderError("Organization Repository Not found")
);
});
});
+310
View File
@@ -0,0 +1,310 @@
require("@testing-library/jest-dom");
const cssToObject = require("css-to-object");
const renderRepoCard = require("../src/renderRepoCard");
const { queryByTestId } = require("@testing-library/dom");
const themes = require("../themes");
const data_repo = {
repository: {
nameWithOwner: "anuraghazra/convoychat",
name: "convoychat",
stargazers: { totalCount: 38000 },
description: "Help us take over the world! React + TS + GraphQL Chat App",
primaryLanguage: {
color: "#2b7489",
id: "MDg6TGFuZ3VhZ2UyODc=",
name: "TypeScript",
},
forkCount: 100,
},
};
describe("Test renderRepoCard", () => {
it("should render correctly", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository);
const [header] = document.getElementsByClassName("header");
expect(header).toHaveTextContent("convoychat");
expect(header).not.toHaveTextContent("anuraghazra");
expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
"Help us take over the world! React + TS + GraphQL Chat App"
);
expect(queryByTestId(document.body, "stargazers")).toHaveTextContent("38k");
expect(queryByTestId(document.body, "forkcount")).toHaveTextContent("100");
expect(queryByTestId(document.body, "lang-name")).toHaveTextContent(
"TypeScript"
);
expect(queryByTestId(document.body, "lang-color")).toHaveAttribute(
"fill",
"#2b7489"
);
});
it("should display username in title (full repo name)", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository, {
show_owner: true,
});
expect(document.getElementsByClassName("header")[0]).toHaveTextContent(
"anuraghazra/convoychat"
);
});
it("should trim description", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
description:
"The quick brown fox jumps over the lazy dog is an English-language pangram—a sentence that contains all of the letters of the English alphabet",
});
expect(
document.getElementsByClassName("description")[0].children[0].textContent
).toBe("The quick brown fox jumps over the lazy dog is an");
expect(
document.getElementsByClassName("description")[0].children[1].textContent
).toBe("English-language pangram—a sentence that contains all");
// Should not trim
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
description: "Small text should not trim",
});
expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
"Small text should not trim"
);
});
it("should render emojis", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
description: "This is a text with a :poop: poo emoji",
});
// poop emoji may not show in all editors but it's there between "a" and "poo"
expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
"This is a text with a 💩 poo emoji"
);
});
it("should shift the text position depending on language length", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
primaryLanguage: {
...data_repo.repository.primaryLanguage,
name: "Jupyter Notebook",
},
});
expect(queryByTestId(document.body, "primary-lang")).toBeInTheDocument();
expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute(
"transform",
"translate(155, 0)"
);
// Small lang
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
primaryLanguage: {
...data_repo.repository.primaryLanguage,
name: "Ruby",
},
});
expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute(
"transform",
"translate(125, 0)"
);
});
it("should hide language if primaryLanguage is null & fallback to correct values", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
primaryLanguage: null,
});
expect(queryByTestId(document.body, "primary-lang")).toBeNull();
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
primaryLanguage: { color: null, name: null },
});
expect(queryByTestId(document.body, "primary-lang")).toBeInTheDocument();
expect(queryByTestId(document.body, "lang-color")).toHaveAttribute(
"fill",
"#333"
);
expect(queryByTestId(document.body, "lang-name")).toHaveTextContent(
"Unspecified"
);
});
it("should render default colors properly", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository);
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const descClassStyles = stylesObject[".description"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe("#2f80ed");
expect(descClassStyles.fill).toBe("#333");
expect(iconClassStyles.fill).toBe("#586069");
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
"#fffefe"
);
});
it("should render custom colors properly", () => {
const customColors = {
title_color: "5a0",
icon_color: "1b998b",
text_color: "9991",
bg_color: "252525",
};
document.body.innerHTML = renderRepoCard(data_repo.repository, {
...customColors,
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const descClassStyles = stylesObject[".description"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe(`#${customColors.title_color}`);
expect(descClassStyles.fill).toBe(`#${customColors.text_color}`);
expect(iconClassStyles.fill).toBe(`#${customColors.icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
"#252525"
);
});
it("should render with all the themes", () => {
Object.keys(themes).forEach((name) => {
document.body.innerHTML = renderRepoCard(data_repo.repository, {
theme: name,
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const descClassStyles = stylesObject[".description"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe(`#${themes[name].title_color}`);
expect(descClassStyles.fill).toBe(`#${themes[name].text_color}`);
expect(iconClassStyles.fill).toBe(`#${themes[name].icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes[name].bg_color}`
);
});
});
it("should render custom colors with themes", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository, {
title_color: "5a0",
theme: "radical",
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const descClassStyles = stylesObject[".description"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe("#5a0");
expect(descClassStyles.fill).toBe(`#${themes.radical.text_color}`);
expect(iconClassStyles.fill).toBe(`#${themes.radical.icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes.radical.bg_color}`
);
});
it("should render custom colors with themes and fallback to default colors if invalid", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository, {
title_color: "invalid color",
text_color: "invalid color",
theme: "radical",
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const descClassStyles = stylesObject[".description"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe(`#${themes.default.title_color}`);
expect(descClassStyles.fill).toBe(`#${themes.default.text_color}`);
expect(iconClassStyles.fill).toBe(`#${themes.radical.icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes.radical.bg_color}`
);
});
it("should not render star count or fork count if either of the are zero", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
stargazers: { totalCount: 0 },
});
expect(queryByTestId(document.body, "stargazers")).toBeNull();
expect(queryByTestId(document.body, "forkcount")).toBeInTheDocument();
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
stargazers: { totalCount: 1 },
forkCount: 0,
});
expect(queryByTestId(document.body, "stargazers")).toBeInTheDocument();
expect(queryByTestId(document.body, "forkcount")).toBeNull();
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
stargazers: { totalCount: 0 },
forkCount: 0,
});
expect(queryByTestId(document.body, "stargazers")).toBeNull();
expect(queryByTestId(document.body, "forkcount")).toBeNull();
});
it("should render badges", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
isArchived: true,
});
expect(queryByTestId(document.body, "badge")).toHaveTextContent("Archived");
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
isTemplate: true,
});
expect(queryByTestId(document.body, "badge")).toHaveTextContent("Template");
});
it("should not render template", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
});
expect(queryByTestId(document.body, "badge")).toBeNull();
});
});
+256
View File
@@ -0,0 +1,256 @@
require("@testing-library/jest-dom");
const cssToObject = require("css-to-object");
const renderStatsCard = require("../src/renderStatsCard");
const {
getByTestId,
queryByTestId,
queryAllByTestId,
} = require("@testing-library/dom");
const themes = require("../themes");
describe("Test renderStatsCard", () => {
const stats = {
name: "Anurag Hazra",
totalStars: 100,
totalCommits: 200,
totalIssues: 300,
totalPRs: 400,
contributedTo: 500,
rank: { level: "A+", score: 40 },
};
it("should render correctly", () => {
document.body.innerHTML = renderStatsCard(stats);
expect(document.getElementsByClassName("header")[0].textContent).toBe(
"Anurag Hazra's GitHub Stats"
);
expect(
document.body.getElementsByTagName("svg")[0].getAttribute("height")
).toBe("195");
expect(getByTestId(document.body, "stars").textContent).toBe("100");
expect(getByTestId(document.body, "commits").textContent).toBe("200");
expect(getByTestId(document.body, "issues").textContent).toBe("300");
expect(getByTestId(document.body, "prs").textContent).toBe("400");
expect(getByTestId(document.body, "contribs").textContent).toBe("500");
expect(queryByTestId(document.body, "card-bg")).toBeInTheDocument();
expect(queryByTestId(document.body, "rank-circle")).toBeInTheDocument();
});
it("should have proper name apostrophe", () => {
document.body.innerHTML = renderStatsCard({ ...stats, name: "Anil Das" });
expect(document.getElementsByClassName("header")[0].textContent).toBe(
"Anil Das' GitHub Stats"
);
document.body.innerHTML = renderStatsCard({ ...stats, name: "Felix" });
expect(document.getElementsByClassName("header")[0].textContent).toBe(
"Felix' GitHub Stats"
);
});
it("should hide individual stats", () => {
document.body.innerHTML = renderStatsCard(stats, {
hide: ["issues", "prs", "contribs"],
});
expect(
document.body.getElementsByTagName("svg")[0].getAttribute("height")
).toBe("150"); // height should be 150 because we clamped it.
expect(queryByTestId(document.body, "stars")).toBeDefined();
expect(queryByTestId(document.body, "commits")).toBeDefined();
expect(queryByTestId(document.body, "issues")).toBeNull();
expect(queryByTestId(document.body, "prs")).toBeNull();
expect(queryByTestId(document.body, "contribs")).toBeNull();
});
it("should hide_border", () => {
document.body.innerHTML = renderStatsCard(stats, { hide_border: true });
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"stroke-opacity",
"0"
);
document.body.innerHTML = renderStatsCard(stats, { hide_border: false });
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"stroke-opacity",
"1"
);
});
it("should hide_rank", () => {
document.body.innerHTML = renderStatsCard(stats, { hide_rank: true });
expect(queryByTestId(document.body, "rank-circle")).not.toBeInTheDocument();
});
it("should render default colors properly", () => {
document.body.innerHTML = renderStatsCard(stats);
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.textContent);
const headerClassStyles = stylesObject[".header"];
const statClassStyles = stylesObject[".stat"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe("#2f80ed");
expect(statClassStyles.fill).toBe("#333");
expect(iconClassStyles.fill).toBe("#4c71f2");
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
"#fffefe"
);
});
it("should render custom colors properly", () => {
const customColors = {
title_color: "5a0",
icon_color: "1b998b",
text_color: "9991",
bg_color: "252525",
};
document.body.innerHTML = renderStatsCard(stats, { ...customColors });
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const statClassStyles = stylesObject[".stat"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe(`#${customColors.title_color}`);
expect(statClassStyles.fill).toBe(`#${customColors.text_color}`);
expect(iconClassStyles.fill).toBe(`#${customColors.icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
"#252525"
);
});
it("should render custom colors with themes", () => {
document.body.innerHTML = renderStatsCard(stats, {
title_color: "5a0",
theme: "radical",
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const statClassStyles = stylesObject[".stat"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe("#5a0");
expect(statClassStyles.fill).toBe(`#${themes.radical.text_color}`);
expect(iconClassStyles.fill).toBe(`#${themes.radical.icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes.radical.bg_color}`
);
});
it("should render with all the themes", () => {
Object.keys(themes).forEach((name) => {
document.body.innerHTML = renderStatsCard(stats, {
theme: name,
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const statClassStyles = stylesObject[".stat"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe(`#${themes[name].title_color}`);
expect(statClassStyles.fill).toBe(`#${themes[name].text_color}`);
expect(iconClassStyles.fill).toBe(`#${themes[name].icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes[name].bg_color}`
);
});
});
it("should render custom colors with themes and fallback to default colors if invalid", () => {
document.body.innerHTML = renderStatsCard(stats, {
title_color: "invalid color",
text_color: "invalid color",
theme: "radical",
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerClassStyles = stylesObject[".header"];
const statClassStyles = stylesObject[".stat"];
const iconClassStyles = stylesObject[".icon"];
expect(headerClassStyles.fill).toBe(`#${themes.default.title_color}`);
expect(statClassStyles.fill).toBe(`#${themes.default.text_color}`);
expect(iconClassStyles.fill).toBe(`#${themes.radical.icon_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes.radical.bg_color}`
);
});
it("should hide the title", () => {
document.body.innerHTML = renderStatsCard(stats, {
hide_title: true,
});
expect(document.getElementsByClassName("header")[0]).toBeUndefined();
expect(document.getElementsByTagName("svg")[0]).toHaveAttribute(
"height",
"165"
);
expect(queryByTestId(document.body, "card-body-content")).toHaveAttribute(
"transform",
"translate(0, -30)"
);
});
it("should not hide the title", () => {
document.body.innerHTML = renderStatsCard(stats, {});
expect(document.getElementsByClassName("header")[0]).toBeDefined();
expect(document.getElementsByTagName("svg")[0]).toHaveAttribute(
"height",
"195"
);
expect(queryByTestId(document.body, "card-body-content")).toHaveAttribute(
"transform",
"translate(0, 0)"
);
});
it("should render icons correctly", () => {
document.body.innerHTML = renderStatsCard(stats, {
show_icons: true,
});
expect(queryAllByTestId(document.body, "icon")[0]).toBeDefined();
expect(queryByTestId(document.body, "stars")).toBeDefined();
expect(
queryByTestId(document.body, "stars").previousElementSibling // the label
).toHaveAttribute("x", "25");
});
it("should not have icons if show_icons is false", () => {
document.body.innerHTML = renderStatsCard(stats, { show_icons: false });
expect(queryAllByTestId(document.body, "icon")[0]).not.toBeDefined();
expect(queryByTestId(document.body, "stars")).toBeDefined();
expect(
queryByTestId(document.body, "stars").previousElementSibling // the label
).not.toHaveAttribute("x");
});
});
+225
View File
@@ -0,0 +1,225 @@
require("@testing-library/jest-dom");
const cssToObject = require("css-to-object");
const renderTopLanguages = require("../src/renderTopLanguages");
const {
getByTestId,
queryByTestId,
queryAllByTestId,
} = require("@testing-library/dom");
const themes = require("../themes");
describe("Test renderTopLanguages", () => {
const langs = {
HTML: {
color: "#0f0",
name: "HTML",
size: 200,
},
javascript: {
color: "#0ff",
name: "javascript",
size: 200,
},
css: {
color: "#ff0",
name: "css",
size: 100,
},
};
it("should render correctly", () => {
document.body.innerHTML = renderTopLanguages(langs);
expect(queryByTestId(document.body, "header")).toHaveTextContent(
"Most Used Languages"
);
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
"HTML"
);
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
"javascript"
);
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
"css"
);
expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute(
"width",
"40%"
);
expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute(
"width",
"40%"
);
expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute(
"width",
"20%"
);
});
it("should hide languages when hide is passed", () => {
document.body.innerHTML = renderTopLanguages(langs, {
hide: ["HTML"],
});
expect(queryAllByTestId(document.body, "lang-name")[0]).toBeInTheDocument(
"javascript"
);
expect(queryAllByTestId(document.body, "lang-name")[1]).toBeInTheDocument(
"css"
);
expect(queryAllByTestId(document.body, "lang-name")[2]).not.toBeDefined();
// multiple languages passed
document.body.innerHTML = renderTopLanguages(langs, {
hide: ["HTML","css"],
});
expect(queryAllByTestId(document.body, "lang-name")[0]).toBeInTheDocument(
"javascript"
);
expect(queryAllByTestId(document.body, "lang-name")[1]).not.toBeDefined();
});
it("should resize the height correctly depending on langs", () => {
document.body.innerHTML = renderTopLanguages(langs, {});
expect(document.querySelector("svg")).toHaveAttribute("height", "205");
document.body.innerHTML = renderTopLanguages(
{
...langs,
python: {
color: "#ff0",
name: "python",
size: 100,
},
},
{}
);
expect(document.querySelector("svg")).toHaveAttribute("height", "245");
});
it("should hide_title", () => {
document.body.innerHTML = renderTopLanguages(langs, { hide_title: false });
expect(document.querySelector("svg")).toHaveAttribute("height", "205");
expect(queryByTestId(document.body, "lang-items")).toHaveAttribute(
"y",
"55"
);
// Lets hide now
document.body.innerHTML = renderTopLanguages(langs, { hide_title: true });
expect(document.querySelector("svg")).toHaveAttribute("height", "175");
expect(queryByTestId(document.body, "header")).not.toBeInTheDocument();
expect(queryByTestId(document.body, "lang-items")).toHaveAttribute(
"y",
"25"
);
});
it("should render with custom width set", () => {
document.body.innerHTML = renderTopLanguages(langs, {});
expect(document.querySelector("svg")).toHaveAttribute("width", "300");
document.body.innerHTML = renderTopLanguages(langs, { card_width: 400 });
expect(document.querySelector("svg")).toHaveAttribute("width", "400");
});
it("should render default colors properly", () => {
document.body.innerHTML = renderTopLanguages(langs);
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.textContent);
const headerStyles = stylesObject[".header"];
const langNameStyles = stylesObject[".lang-name"];
expect(headerStyles.fill).toBe("#2f80ed");
expect(langNameStyles.fill).toBe("#333");
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
"#fffefe"
);
});
it("should render custom colors properly", () => {
const customColors = {
title_color: "5a0",
icon_color: "1b998b",
text_color: "9991",
bg_color: "252525",
};
document.body.innerHTML = renderTopLanguages(langs, { ...customColors });
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerStyles = stylesObject[".header"];
const langNameStyles = stylesObject[".lang-name"];
expect(headerStyles.fill).toBe(`#${customColors.title_color}`);
expect(langNameStyles.fill).toBe(`#${customColors.text_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
"#252525"
);
});
it("should render custom colors with themes", () => {
document.body.innerHTML = renderTopLanguages(langs, {
title_color: "5a0",
theme: "radical",
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerStyles = stylesObject[".header"];
const langNameStyles = stylesObject[".lang-name"];
expect(headerStyles.fill).toBe("#5a0");
expect(langNameStyles.fill).toBe(`#${themes.radical.text_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes.radical.bg_color}`
);
});
it("should render with all the themes", () => {
Object.keys(themes).forEach((name) => {
document.body.innerHTML = renderTopLanguages(langs, {
theme: name,
});
const styleTag = document.querySelector("style");
const stylesObject = cssToObject(styleTag.innerHTML);
const headerStyles = stylesObject[".header"];
const langNameStyles = stylesObject[".lang-name"];
expect(headerStyles.fill).toBe(`#${themes[name].title_color}`);
expect(langNameStyles.fill).toBe(`#${themes[name].text_color}`);
expect(queryByTestId(document.body, "card-bg")).toHaveAttribute(
"fill",
`#${themes[name].bg_color}`
);
});
});
it('should render with layout compact', () => {
document.body.innerHTML = renderTopLanguages(langs, {layout: 'compact'});
expect(queryByTestId(document.body, "header")).toHaveTextContent("Most Used Languages");
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent("HTML 40.00%");
expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute("width","120.00");
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent("javascript 40.00%");
expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute("width","120.00");
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent("css 20.00%");
expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute("width","60.00");
})
});
+51
View File
@@ -0,0 +1,51 @@
require("@testing-library/jest-dom");
const retryer = require("../src/retryer");
const { logger } = require("../src/utils");
const fetcher = jest.fn((variables, token) => {
logger.log(variables, token);
return new Promise((res, rej) => res({ data: "ok" }));
});
const fetcherFail = jest.fn(() => {
return new Promise((res, rej) =>
res({ data: { errors: [{ type: "RATE_LIMITED" }] } })
);
});
const fetcherFailOnSecondTry = jest.fn((_vars, _token, retries) => {
return new Promise((res, rej) => {
// faking rate limit
if (retries < 1) {
return res({ data: { errors: [{ type: "RATE_LIMITED" }] } });
}
return res({ data: "ok" });
});
});
describe("Test Retryer", () => {
it("retryer should return value and have zero retries on first try", async () => {
let res = await retryer(fetcher, {});
expect(fetcher).toBeCalledTimes(1);
expect(res).toStrictEqual({ data: "ok" });
});
it("retryer should return value and have 2 retries", async () => {
let res = await retryer(fetcherFailOnSecondTry, {});
expect(fetcherFailOnSecondTry).toBeCalledTimes(2);
expect(res).toStrictEqual({ data: "ok" });
});
it("retryer should throw error if maximum retries reached", async () => {
let res;
try {
res = await retryer(fetcherFail, {});
} catch (err) {
expect(fetcherFail).toBeCalledTimes(8);
expect(err.message).toBe("Maximum retries exceeded");
}
});
});
+142
View File
@@ -0,0 +1,142 @@
require("@testing-library/jest-dom");
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
const topLangs = require("../api/top-langs");
const renderTopLanguages = require("../src/renderTopLanguages");
const { renderError } = require("../src/utils");
const data_langs = {
data: {
user: {
repositories: {
nodes: [
{
languages: {
edges: [{ size: 150, node: { color: "#0f0", name: "HTML" } }],
},
},
{
languages: {
edges: [{ size: 100, node: { color: "#0f0", name: "HTML" } }],
},
},
{
languages: {
edges: [
{ size: 100, node: { color: "#0ff", name: "javascript" } },
],
},
},
{
languages: {
edges: [
{ size: 100, node: { color: "#0ff", name: "javascript" } },
],
},
},
],
},
},
},
};
const error = {
errors: [
{
type: "NOT_FOUND",
path: ["user"],
locations: [],
message: "Could not fetch user",
},
],
};
const langs = {
HTML: {
color: "#0f0",
name: "HTML",
size: 250,
},
javascript: {
color: "#0ff",
name: "javascript",
size: 200,
},
};
const mock = new MockAdapter(axios);
afterEach(() => {
mock.reset();
});
describe("Test /api/top-langs", () => {
it("should test the request", async () => {
const req = {
query: {
username: "anuraghazra",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data_langs);
await topLangs(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(renderTopLanguages(langs));
});
it("should work with the query options", async () => {
const req = {
query: {
username: "anuraghazra",
hide_title: true,
card_width: 100,
title_color: "fff",
icon_color: "fff",
text_color: "fff",
bg_color: "fff",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data_langs);
await topLangs(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(
renderTopLanguages(langs, {
hide_title: true,
card_width: 100,
title_color: "fff",
icon_color: "fff",
text_color: "fff",
bg_color: "fff",
})
);
});
it("should render error card on error", async () => {
const req = {
query: {
username: "anuraghazra",
},
};
const res = {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, error);
await topLangs(req, res);
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
expect(res.send).toBeCalledWith(renderError(error.errors[0].message));
});
});
+136
View File
@@ -0,0 +1,136 @@
require("@testing-library/jest-dom");
const {
kFormatter,
encodeHTML,
renderError,
FlexLayout,
getCardColors,
wrapTextMultiline,
} = require("../src/utils");
const { queryByTestId } = require("@testing-library/dom");
describe("Test utils.js", () => {
it("should test kFormatter", () => {
expect(kFormatter(1)).toBe(1);
expect(kFormatter(-1)).toBe(-1);
expect(kFormatter(500)).toBe(500);
expect(kFormatter(1000)).toBe("1k");
expect(kFormatter(10000)).toBe("10k");
expect(kFormatter(12345)).toBe("12.3k");
expect(kFormatter(9900000)).toBe("9900k");
});
it("should test encodeHTML", () => {
expect(encodeHTML(`<html>hello world<,.#4^&^@%!))`)).toBe(
"&#60;html&#62;hello world&#60;,.#4^&#38;^@%!))"
);
});
it("should test renderError", () => {
document.body.innerHTML = renderError("Something went wrong");
expect(
queryByTestId(document.body, "message").children[0]
).toHaveTextContent(/Something went wrong/gim);
expect(queryByTestId(document.body, "message").children[1]).toBeEmpty(2);
// Secondary message
document.body.innerHTML = renderError(
"Something went wrong",
"Secondary Message"
);
expect(
queryByTestId(document.body, "message").children[1]
).toHaveTextContent(/Secondary Message/gim);
});
it("should test FlexLayout", () => {
const layout = FlexLayout({
items: ["<text>1</text>", "<text>2</text>"],
gap: 60,
}).join("");
expect(layout).toBe(
`<g transform=\"translate(0, 0)\"><text>1</text></g><g transform=\"translate(60, 0)\"><text>2</text></g>`
);
const columns = FlexLayout({
items: ["<text>1</text>", "<text>2</text>"],
gap: 60,
direction: "column",
}).join("");
expect(columns).toBe(
`<g transform=\"translate(0, 0)\"><text>1</text></g><g transform=\"translate(0, 60)\"><text>2</text></g>`
);
});
it("getCardColors: should return expected values", () => {
let colors = getCardColors({
title_color: "f00",
text_color: "0f0",
icon_color: "00f",
bg_color: "fff",
theme: "dark",
});
expect(colors).toStrictEqual({
titleColor: "#f00",
textColor: "#0f0",
iconColor: "#00f",
bgColor: "#fff",
});
});
it("getCardColors: should fallback to default colors if color is invalid", () => {
let colors = getCardColors({
title_color: "invalidcolor",
text_color: "0f0",
icon_color: "00f",
bg_color: "fff",
theme: "dark",
});
expect(colors).toStrictEqual({
titleColor: "#2f80ed",
textColor: "#0f0",
iconColor: "#00f",
bgColor: "#fff",
});
});
it("getCardColors: should fallback to specified theme colors if is not defined", () => {
let colors = getCardColors({
theme: "dark",
});
expect(colors).toStrictEqual({
titleColor: "#fff",
textColor: "#9f9f9f",
iconColor: "#79ff97",
bgColor: "#151515",
});
});
});
describe("wrapTextMultiline", () => {
it("should not wrap small texts", () => {
{
let multiLineText = wrapTextMultiline("Small text should not wrap");
expect(multiLineText).toEqual(["Small text should not wrap"]);
}
});
it("should wrap large texts", () => {
let multiLineText = wrapTextMultiline(
"Hello world long long long text",
20,
3
);
expect(multiLineText).toEqual(["Hello world long", "long long text"]);
});
it("should wrap large texts and limit max lines", () => {
let multiLineText = wrapTextMultiline(
"Hello world long long long text",
10,
2
);
expect(multiLineText).toEqual(["Hello", "world long..."]);
});
});
+90
View File
@@ -0,0 +1,90 @@
## Available Themes
<!-- DO NOT EDIT THIS FILE DIRECTLY -->
With inbuilt themes you can customize the look of the card without doing any manual customization.
Use `?theme=THEME_NAME` parameter like so :-
```md
![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&theme=dark&show_icons=true)
```
## Stats
> These themes work both for the Stats Card and Repo Card.
| | | |
| :--: | :--: | :--: |
| `default` ![default][default] | `dark` ![dark][dark] | `radical` ![radical][radical] |
| `merko` ![merko][merko] | `gruvbox` ![gruvbox][gruvbox] | `tokyonight` ![tokyonight][tokyonight] |
| `onedark` ![onedark][onedark] | `cobalt` ![cobalt][cobalt] | `synthwave` ![synthwave][synthwave] |
| `highcontrast` ![highcontrast][highcontrast] | `dracula` ![dracula][dracula] | `prussian` ![prussian][prussian] |
| `monokai` ![monokai][monokai] | `vue` ![vue][vue] | `shades-of-purple` ![shades-of-purple][shades-of-purple] |
| `nightowl` ![nightowl][nightowl] | `buefy` ![buefy][buefy] | `blue-green` ![blue-green][blue-green] |
| `algolia` ![algolia][algolia] | `great-gatsby` ![great-gatsby][great-gatsby] | [Add your theme][add-theme] |
## Repo Card
> These themes work both for the Stats Card and Repo Card.
| | | |
| :--: | :--: | :--: |
| `default_repocard` ![default_repocard][default_repocard_repo] | `dark` ![dark][dark_repo] | `radical` ![radical][radical_repo] |
| `merko` ![merko][merko_repo] | `gruvbox` ![gruvbox][gruvbox_repo] | `tokyonight` ![tokyonight][tokyonight_repo] |
| `onedark` ![onedark][onedark_repo] | `cobalt` ![cobalt][cobalt_repo] | `synthwave` ![synthwave][synthwave_repo] |
| `highcontrast` ![highcontrast][highcontrast_repo] | `dracula` ![dracula][dracula_repo] | `prussian` ![prussian][prussian_repo] |
| `monokai` ![monokai][monokai_repo] | `vue` ![vue][vue_repo] | `shades-of-purple` ![shades-of-purple][shades-of-purple_repo] |
| `nightowl` ![nightowl][nightowl_repo] | `buefy` ![buefy][buefy_repo] | `blue-green` ![blue-green][blue-green_repo] |
| `algolia` ![algolia][algolia_repo] | `great-gatsby` ![great-gatsby][great-gatsby_repo] | [Add your theme][add-theme] |
[default]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=default
[default_repocard]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=default_repocard
[dark]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=dark
[radical]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=radical
[merko]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=merko
[gruvbox]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=gruvbox
[tokyonight]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=tokyonight
[onedark]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=onedark
[cobalt]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=cobalt
[synthwave]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=synthwave
[highcontrast]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=highcontrast
[dracula]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=dracula
[prussian]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=prussian
[monokai]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=monokai
[vue]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=vue
[shades-of-purple]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=shades-of-purple
[nightowl]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=nightowl
[buefy]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=buefy
[blue-green]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=blue-green
[algolia]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=algolia
[great-gatsby]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=great-gatsby
[default_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=default
[default_repocard_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=default_repocard
[dark_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=dark
[radical_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=radical
[merko_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=merko
[gruvbox_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=gruvbox
[tokyonight_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=tokyonight
[onedark_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=onedark
[cobalt_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=cobalt
[synthwave_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=synthwave
[highcontrast_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=highcontrast
[dracula_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=dracula
[prussian_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=prussian
[monokai_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=monokai
[vue_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=vue
[shades-of-purple_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=shades-of-purple
[nightowl_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=nightowl
[buefy_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=buefy
[blue-green_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=blue-green
[algolia_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=algolia
[great-gatsby_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=great-gatsby
[add-theme]: https://github.com/anuraghazra/github-readme-stats/edit/master/themes/index.js
Wanted to add a new theme? Consider reading the [contribution guidelines](../CONTRIBUTING.md#themes-contribution) :D
+130
View File
@@ -0,0 +1,130 @@
const themes = {
default: {
title_color: "2f80ed",
icon_color: "4c71f2",
text_color: "333",
bg_color: "fffefe",
},
default_repocard: {
title_color: "2f80ed",
icon_color: "586069", // icon color is different
text_color: "333",
bg_color: "fffefe",
},
dark: {
title_color: "fff",
icon_color: "79ff97",
text_color: "9f9f9f",
bg_color: "151515",
},
radical: {
title_color: "fe428e",
icon_color: "f8d847",
text_color: "a9fef7",
bg_color: "141321",
},
merko: {
title_color: "abd200",
icon_color: "b7d364",
text_color: "68b587",
bg_color: "0a0f0b",
},
gruvbox: {
title_color: "fabd2f",
icon_color: "fe8019",
text_color: "8ec07c",
bg_color: "282828",
},
tokyonight: {
title_color: "70a5fd",
icon_color: "bf91f3",
text_color: "38bdae",
bg_color: "1a1b27",
},
onedark: {
title_color: "e4bf7a",
icon_color: "8eb573",
text_color: "df6d74",
bg_color: "282c34",
},
cobalt: {
title_color: "e683d9",
icon_color: "0480ef",
text_color: "75eeb2",
bg_color: "193549",
},
synthwave: {
title_color: "e2e9ec",
icon_color: "ef8539",
text_color: "e5289e",
bg_color: "2b213a",
},
highcontrast: {
title_color: "e7f216",
icon_color: "00ffff",
text_color: "fff",
bg_color: "000",
},
dracula: {
title_color: "ff6e96",
icon_color: "79dafa",
text_color: "f8f8f2",
bg_color: "282a36",
},
prussian: {
title_color: "bddfff",
icon_color: "38a0ff",
text_color: "6e93b5",
bg_color: "172f45",
},
monokai: {
title_color: "eb1f6a",
icon_color: "e28905",
text_color: "f1f1eb",
bg_color: "272822",
},
vue: {
title_color: "41b883",
icon_color: "41b883",
text_color: "273849",
bg_color: "fffefe",
},
"shades-of-purple": {
title_color: "fad000",
icon_color: "b362ff",
text_color: "a599e9",
bg_color: "2d2b55",
},
nightowl: {
title_color: "c792ea",
icon_color: "ffeb95",
text_color: "7fdbca",
bg_color: "011627",
},
buefy: {
title_color: "7957d5",
icon_color: "ff3860",
text_color: "363636",
bg_color: "ffffff",
},
"blue-green": {
title_color: "2f97c1",
icon_color: "f5b700",
text_color: "0cf574",
bg_color: "040f0f",
},
"algolia": {
title_color: "00AEFF",
icon_color: "2DDE98",
text_color: "FFFFFF",
bg_color: "050F2C",
},
"great-gatsby":{
title_color: "ffa726",
icon_color: "ffb74d",
text_color: "ffd95b",
bg_color: "000000",
},
};
module.exports = themes;
-28
View File
@@ -1,28 +0,0 @@
const renderError = (message) => {
return `
<svg width="495" height="100" viewBox="0 0 495 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.text { font: 600 16px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
.small { font: 600 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #252525 }
</style>
<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
<text x="25" y="45" class="text">Something went wrong! file an issue at https://git.io/JJmN9</text>
<text x="25" y="65" class="text small">${message}</text>
</svg>
`;
};
// https://stackoverflow.com/a/48073476/10629172
function encodeHTML(str) {
return str.replace(/[\u00A0-\u9999<>&](?!#)/gim, function (i) {
return "&#" + i.charCodeAt(0) + ";";
});
}
function kFormatter(num) {
return Math.abs(num) > 999
? Math.sign(num) * (Math.abs(num) / 1000).toFixed(1) + "k"
: Math.sign(num) * Math.abs(num);
}
module.exports = { renderError, kFormatter, encodeHTML };
+8
View File
@@ -0,0 +1,8 @@
{
"redirects": [
{
"source": "/",
"destination": "https://github.com/anuraghazra/github-readme-stats"
}
]
}