diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000000..c3708f4f89ac9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +node_modules +*.json +*.md \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000000000..6b6938ff65681 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "none", + "useTabs": false, + "endOfLine": "lf", + "proseWrap": "always" +} diff --git a/api/pin.js b/api/pin.js index c09b2d6eaa092..dd7dbb953a218 100644 --- a/api/pin.js +++ b/api/pin.js @@ -3,7 +3,7 @@ const { renderError, parseBoolean, clampValue, - CONSTANTS, + CONSTANTS } = require("../src/common/utils"); const fetchRepo = require("../src/fetchers/repo-fetcher"); const renderRepoCard = require("../src/cards/repo-card"); @@ -13,13 +13,14 @@ module.exports = async (req, res) => { const { username, repo, + hide_border, title_color, icon_color, text_color, bg_color, theme, show_owner, - cache_seconds, + cache_seconds } = req.query; let repoData; @@ -56,12 +57,13 @@ module.exports = async (req, res) => { return res.send( renderRepoCard(repoData, { + hide_border, title_color, icon_color, text_color, bg_color, theme, - show_owner: parseBoolean(show_owner), + show_owner: parseBoolean(show_owner) }) ); } catch (err) { diff --git a/api/top-langs.js b/api/top-langs.js index 59692cd2a81be..ff0268049dbfa 100644 --- a/api/top-langs.js +++ b/api/top-langs.js @@ -4,7 +4,7 @@ const { clampValue, parseBoolean, parseArray, - CONSTANTS, + CONSTANTS } = require("../src/common/utils"); const fetchTopLanguages = require("../src/fetchers/top-languages-fetcher"); const renderTopLanguages = require("../src/cards/top-languages-card"); @@ -24,9 +24,10 @@ module.exports = async (req, res) => { cache_seconds, layout, langs_count, + count_forks, + exclude_repo } = req.query; let topLangs; - res.setHeader("Content-Type", "image/svg+xml"); if (blacklist.includes(username)) { @@ -34,7 +35,12 @@ module.exports = async (req, res) => { } try { - topLangs = await fetchTopLanguages(username, langs_count); + topLangs = await fetchTopLanguages( + username, + langs_count, + count_forks, + parseArray(exclude_repo) + ); const cacheSeconds = clampValue( parseInt(cache_seconds || CONSTANTS.TWO_HOURS, 10), @@ -54,7 +60,7 @@ module.exports = async (req, res) => { text_color, bg_color, theme, - layout, + layout }) ); } catch (err) { diff --git a/readme.md b/readme.md index 7ef0bef27156c..d23fb03546eda 100644 --- a/readme.md +++ b/readme.md @@ -133,6 +133,7 @@ You can customize the appearance of your `Stats Card` or `Repo Card` however you - `text_color` - Body text color _(hex color)_ - `icon_color` - Icons color if available _(hex color)_ - `bg_color` - Card's background color _(hex color)_ **or** a gradient in the form of _angle,start,end_ +- `hide_border` - Hides the card's border _(boolean)_ - `theme` - name of the theme, choose from [all available themes](./themes/README.md) - `cache_seconds` - set the cache header manually _(min: 1800, max: 86400)_ @@ -166,16 +167,17 @@ You can provide multiple comma-separated values in bg_color option to render a g - `hide_title` - _(boolean)_ - `layout` - Switch between two available layouts `default` & `compact` - `card_width` - Set the card's width manually _(number)_ +- `count_forks` - Whether to count languages of forks, default: false _(boolean)_ +- `exclude_repo` - Exclude specified repositories _(Comma-separated values)_ - `langs_count` - Show more languages on the card, between 1-10, defaults to 5 _(number)_ -> :warning: **Important:** -> Language names should be uri-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) +> :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.) #### Wakatime Card Exclusive Options: - `hide_title` - _(boolean)_ -- `hide_border` - _(boolean)_ - `line_height` - Sets the line-height between text _(number)_ - `hide_progress` - Hides the progress bar and percentage _(boolean)_ @@ -221,6 +223,22 @@ Endpoint: `api/top-langs?username=anuraghazra` [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats) ``` +### Count languages of forks + +You can use `?count_forks=true` parameter to count languages of forks. + +```md +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&count_forks=true)](https://github.com/anuraghazra/github-readme-stats) +``` + +### Exclude individual repositories + +You can use `?exclude_repo=repo1,repo2` parameter to exclude individual repositories. + +```md +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&exclude_repo=reakit,gatsby)](https://github.com/anuraghazra/github-readme-stats) +``` + ### Hide individual languages You can use `?hide=language1,language2` parameter to hide individual languages. @@ -342,16 +360,16 @@ NOTE: Since [#58](https://github.com/anuraghazra/github-readme-stats/pull/58) we Guide on setting up Vercel 🔨 1. Go to [vercel.com](https://vercel.com/) -1. Click on `Log in` +1. Click on `Log in` ![](https://files.catbox.moe/tct1wg.png) -1. Sign in with GitHub by pressing `Continue with GitHub` +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` +1. Select `Import Project` ![](https://files.catbox.moe/qckos0.png) -1. Select `Import Git Repository` +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) diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 233812b4b0db9..d04b1583fce82 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -3,7 +3,7 @@ const { encodeHTML, getCardColors, FlexLayout, - wrapTextMultiline, + wrapTextMultiline } = require("../common/utils"); const icons = require("../common/icons"); const Card = require("../common/Card"); @@ -18,15 +18,16 @@ const renderRepoCard = (repo, options = {}) => { stargazers, isArchived, isTemplate, - forkCount, + forkCount } = repo; const { + hide_border, title_color, icon_color, text_color, bg_color, show_owner, - theme = "default_repocard", + theme = "default_repocard" } = options; const header = show_owner ? nameWithOwner : name; @@ -55,7 +56,7 @@ const renderRepoCard = (repo, options = {}) => { icon_color, text_color, bg_color, - theme, + theme }); const totalStars = kFormatter(stargazers.totalCount); @@ -101,7 +102,7 @@ const renderRepoCard = (repo, options = {}) => { const starAndForkCount = FlexLayout({ items: [svgStars, svgForks], - gap: 65, + gap: 65 }).join(""); const card = new Card({ @@ -113,12 +114,12 @@ const renderRepoCard = (repo, options = {}) => { titleColor, textColor, iconColor, - bgColor, - }, + bgColor + } }); card.disableAnimations(); - card.setHideBorder(false); + card.setHideBorder(hide_border); card.setHideTitle(false); card.setCSS(` .description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} } @@ -147,7 +148,7 @@ const renderRepoCard = (repo, options = {}) => { ${svgLanguage} ${starAndForkCount} diff --git a/src/common/retryer.js b/src/common/retryer.js index 592463ef2a09c..7b49d93068b78 100644 --- a/src/common/retryer.js +++ b/src/common/retryer.js @@ -1,6 +1,6 @@ const { logger, CustomError } = require("../common/utils"); -const retryer = async (fetcher, variables, retries = 0) => { +const retryer = async (fetcher, variables, retries = 0, count_forks) => { if (retries > 7) { throw new CustomError("Maximum retries exceeded", CustomError.MAX_RETRY); } @@ -9,7 +9,8 @@ const retryer = async (fetcher, variables, retries = 0) => { let response = await fetcher( variables, process.env[`PAT_${retries + 1}`], - retries + retries, + count_forks ); // prettier-ignore @@ -21,7 +22,7 @@ const retryer = async (fetcher, variables, retries = 0) => { logger.log(`PAT_${retries + 1} Failed`); retries++; // directly return from the function - return retryer(fetcher, variables, retries); + return retryer(fetcher, variables, retries, count_forks); } // finally return the response @@ -35,7 +36,7 @@ const retryer = async (fetcher, variables, retries = 0) => { logger.log(`PAT_${retries + 1} Failed`); retries++; // directly return from the function - return retryer(fetcher, variables, retries); + return retryer(fetcher, variables, retries, count_forks); } } }; diff --git a/src/fetchers/top-languages-fetcher.js b/src/fetchers/top-languages-fetcher.js index 51ed8a87b12b4..a9038c56c3ca8 100644 --- a/src/fetchers/top-languages-fetcher.js +++ b/src/fetchers/top-languages-fetcher.js @@ -2,15 +2,17 @@ const { request, logger, clampValue } = require("../common/utils"); const retryer = require("../common/retryer"); require("dotenv").config(); -const fetcher = (variables, token) => { +const fetcher = (variables, token, retries, count_forks) => { + const forks = count_forks == "true" ? `` : `isFork: false, `; return request( { query: ` query userInfo($login: String!) { user(login: $login) { - # fetch only owner repos & not forks - repositories(ownerAffiliations: OWNER, isFork: false, first: 100) { + # fetch only owner repos + repositories(ownerAffiliations: OWNER, ${forks}first: 100) { nodes { + name languages(first: 10, orderBy: {field: SIZE, direction: DESC}) { edges { size @@ -25,20 +27,25 @@ const fetcher = (variables, token) => { } } `, - variables, + variables }, { - Authorization: `bearer ${token}`, + Authorization: `bearer ${token}` } ); }; -async function fetchTopLanguages(username, langsCount = 5) { +async function fetchTopLanguages( + username, + langsCount = 5, + count_forks = false, + exclude_repo = [] +) { if (!username) throw Error("Invalid username"); langsCount = clampValue(parseInt(langsCount), 1, 10); - const res = await retryer(fetcher, { login: username }); + const res = await retryer(fetcher, { login: username }, 0, count_forks); if (res.data.errors) { logger.error(res.data.errors); @@ -46,6 +53,22 @@ async function fetchTopLanguages(username, langsCount = 5) { } let repoNodes = res.data.data.user.repositories.nodes; + let repoToHide = {}; + + // populate repoToHide map for quick lookup + // while filtering out + if (exclude_repo) { + exclude_repo.forEach((langName) => { + repoToHide[langName] = true; + }); + } + + // filter out repositories to be hidden + repoNodes = repoNodes + .sort((a, b) => b.size - a.size) + .filter((name) => { + return !repoToHide[name.name]; + }); repoNodes = repoNodes .filter((node) => { @@ -69,8 +92,8 @@ async function fetchTopLanguages(username, langsCount = 5) { [prev.node.name]: { name: prev.node.name, color: prev.node.color, - size: langSize, - }, + size: langSize + } }; }, {});