Compare commits

..
Author SHA1 Message Date
Alexandr 546921aaa9 docs: add translations contribution guide 2023-10-14 22:26:39 +03:00
Rick Staa 4fd52e6f64 ci: fix small theme preview script typo (#3355) 2023-10-14 00:08:07 +03:00
Rick Staa 7a6d7b4b73 ci: fix 'update-langs' workflow permissions (#3352) 2023-10-13 23:57:33 +03:00
Rick Staa ee7cf1fec4 docs: add uptime badge (#3350) 2023-10-13 23:53:07 +03:00
Alexandr Garbuzov 7c5e998725 ci: fix master branch name in CodeQL analysis workflow (#3354) 2023-10-13 23:50:03 +03:00
Alexandr Garbuzov cd9698bea5 infra: enable no-mixed-spaces-and-tabs eslint rule (#3285) 2023-10-13 22:25:34 +03:00
Alexandr Garbuzov b753d49158 infra: enable radix eslint rule (#3261) 2023-10-13 22:21:12 +03:00
Alexandr Garbuzov 1c07f4142c feature: fetch only requested data from GitHub GraphQL API to reduce load (#3208)
* feature: fetch only requested data from GitHub GraphQL API to reduce load

* dev

* dev
2023-10-13 22:14:06 +03:00
Rick Staa 7b1b78df2f Revert "Update generate-theme-doc.js (#3308)" (#3353)
This reverts commit 28b65928d2.
2023-10-13 20:34:42 +05:30
sahilpawar01andrickstaa 28b65928d2 Update generate-theme-doc.js (#3308)
* Update generate-theme-doc.js

Optimize README generation code for efficiency and readability.

* fix(scripts): fix some small bugs

This commit fixes some small bugs that were still present in the script
code.

---------

Co-authored-by: rickstaa <rick.staa@outlook.com>
2023-10-13 13:36:06 +02:00
Alexandr Garbuzov 428e97bfcc Add performance tests base (#3141)
* Add basic bench tests

* dev
2023-10-13 11:27:04 +02:00
Alexandr Garbuzovandrickstaa 0fd1ea3ce9 feature: do not aks user's to open issues on upstream API errors (#3273)
Co-authored-by: rickstaa <rick.staa@outlook.com>
2023-10-13 11:16:14 +02:00
Rick Staa a39785189c docs: fix relative README links (#3067) 2023-10-12 19:57:52 +02:00
18 changed files with 560 additions and 84 deletions
+2 -2
View File
@@ -99,7 +99,7 @@
"location": "start" "location": "start"
}], }],
"no-with": "warn", "no-with": "warn",
// "radix": "warn", "radix": "warn",
// "vars-on-top": "error", // "vars-on-top": "error",
// Enforces the style of wrapped functions // Enforces the style of wrapped functions
@@ -193,7 +193,7 @@
// "no-continue": "off", // "no-continue": "off",
// "no-inline-comments": "off", // "no-inline-comments": "off",
// "no-lonely-if": "warn", // "no-lonely-if": "warn",
// "no-mixed-spaces-and-tabs": "warn", "no-mixed-spaces-and-tabs": "warn",
"no-multiple-empty-lines": "warn", "no-multiple-empty-lines": "warn",
// "no-negated-condition": "off", // "no-negated-condition": "off",
// "no-nested-ternary": "warn", // "no-nested-ternary": "warn",
+4 -2
View File
@@ -2,9 +2,11 @@ name: "Static code analysis workflow (CodeQL)"
on: on:
push: push:
branches: [main] branches:
- master
pull_request: pull_request:
branches: [main] branches:
- master
permissions: permissions:
actions: read actions: read
+4
View File
@@ -35,6 +35,10 @@ jobs:
run: | run: |
npm run lint npm run lint
- name: Run bench tests
run: |
npm run bench
- name: Run Prettier - name: Run Prettier
run: | run: |
npm run format:check npm run format:check
+1 -1
View File
@@ -15,7 +15,7 @@ on:
permissions: permissions:
actions: read actions: read
checks: read checks: read
contents: read contents: write
deployments: read deployments: read
issues: read issues: read
discussions: read discussions: read
+1
View File
@@ -4,6 +4,7 @@ node_modules
*.lock *.lock
.idea/ .idea/
coverage coverage
benchmarks
vercel_token vercel_token
# IDE # IDE
+6
View File
@@ -58,6 +58,12 @@ GitHub Readme Stats supports custom theming, and you can also contribute new the
To contribute your theme you need to edit the [themes/index.js](./themes/index.js) file and add it at the end of the file. To contribute your theme you need to edit the [themes/index.js](./themes/index.js) file and add it at the end of the file.
## Translations Contribution
GitHub Readme Stats supports multiple languages, if we are missing your language, you can contribute it! You can check the currently supported languages [here](./readme.md#available-locales).
To contribute your language you need to edit the [src/translations.js](./src/translations.js) file and add new property to each object where the key is the language code in [ISO 639-1 standard](https://www.andiamo.co.uk/resources/iso-language-codes/) and the value is the translated string.
## Any contributions you make will be under the MIT Software License ## 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. 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.
+6 -1
View File
@@ -50,10 +50,15 @@ export default async (req, res) => {
} }
try { try {
const showStats = parseArray(show);
const stats = await fetchStats( const stats = await fetchStats(
username, username,
parseBoolean(include_all_commits), parseBoolean(include_all_commits),
parseArray(exclude_repo), parseArray(exclude_repo),
showStats.includes("prs_merged") ||
showStats.includes("prs_merged_percentage"),
showStats.includes("discussions_started"),
showStats.includes("discussions_answered"),
); );
let cacheSeconds = clampValue( let cacheSeconds = clampValue(
@@ -96,7 +101,7 @@ export default async (req, res) => {
locale: locale ? locale.toLowerCase() : null, locale: locale ? locale.toLowerCase() : null,
disable_animations: parseBoolean(disable_animations), disable_animations: parseBoolean(disable_animations),
rank_icon, rank_icon,
show: parseArray(show), show: showStats,
}), }),
); );
} catch (err) { } catch (err) {
+16
View File
@@ -0,0 +1,16 @@
export default {
// Jest-bench need its own test environment to function
testEnvironment: "jest-bench/environment",
testEnvironmentOptions: {
// still Jest-bench environment will run your environment if you specify it here
testEnvironment: "jest-environment-node",
testEnvironmentOptions: {
// specify any option for your environment
},
},
// always include "default" reporter along with Jest-bench reporter
// for error reporting
reporters: ["default", "jest-bench/reporter"],
// will pick up "*.bench.js" file.
testRegex: "(\\.bench)\\.(ts|tsx|js)$",
};
+240
View File
@@ -29,6 +29,7 @@
"hjson": "^3.2.2", "hjson": "^3.2.2",
"husky": "^8.0.3", "husky": "^8.0.3",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-bench": "^29.4.1",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lint-staged": "^14.0.1", "lint-staged": "^14.0.1",
@@ -1925,6 +1926,16 @@
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==",
"dev": true "dev": true
}, },
"node_modules/benchmark": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
"integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==",
"dev": true,
"dependencies": {
"lodash": "^4.17.4",
"platform": "^1.3.3"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -4006,6 +4017,23 @@
} }
} }
}, },
"node_modules/jest-bench": {
"version": "29.4.1",
"resolved": "https://registry.npmjs.org/jest-bench/-/jest-bench-29.4.1.tgz",
"integrity": "sha512-CbhGPgHX+b4AQKnxz/iziVHpgLG+eoGKIvIkOH+VmuOLxme7klbgvOpNB0Ab+XNq/u/AmOlKK5cd1dGuaN4iEA==",
"dev": true,
"dependencies": {
"@jest/globals": "^29.4.1",
"@jest/reporters": "^29.4.1",
"benchmark": "^2.1.4",
"chalk": "^4.1.0",
"lodash": "^4.17.20",
"ndjson": "^2.0.0"
},
"peerDependencies": {
"jest": "^29.4.1"
}
},
"node_modules/jest-changed-files": { "node_modules/jest-changed-files": {
"version": "29.7.0", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
@@ -4976,6 +5004,12 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true "dev": true
}, },
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true
},
"node_modules/json5": { "node_modules/json5": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -5593,6 +5627,15 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -5605,6 +5648,25 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"node_modules/ndjson": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz",
"integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==",
"dev": true,
"dependencies": {
"json-stringify-safe": "^5.0.1",
"minimist": "^1.2.5",
"readable-stream": "^3.6.0",
"split2": "^3.0.0",
"through2": "^4.0.0"
},
"bin": {
"ndjson": "cli.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.6.7", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -5962,6 +6024,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/platform": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
"dev": true
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -6093,6 +6161,20 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true "dev": true
}, },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/redent": { "node_modules/redent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@@ -6261,6 +6343,26 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -6403,6 +6505,15 @@
"source-map": "^0.6.0" "source-map": "^0.6.0"
} }
}, },
"node_modules/split2": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
"dev": true,
"dependencies": {
"readable-stream": "^3.0.0"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -6433,6 +6544,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-argv": { "node_modules/string-argv": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
@@ -6573,6 +6693,15 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true "dev": true
}, },
"node_modules/through2": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
"dev": true,
"dependencies": {
"readable-stream": "3"
}
},
"node_modules/tmpl": { "node_modules/tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -6738,6 +6867,12 @@
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"node_modules/uuid": { "node_modules/uuid": {
"version": "8.3.2", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -8533,6 +8668,16 @@
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==",
"dev": true "dev": true
}, },
"benchmark": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
"integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==",
"dev": true,
"requires": {
"lodash": "^4.17.4",
"platform": "^1.3.3"
}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -9998,6 +10143,20 @@
"jest-cli": "^29.7.0" "jest-cli": "^29.7.0"
} }
}, },
"jest-bench": {
"version": "29.4.1",
"resolved": "https://registry.npmjs.org/jest-bench/-/jest-bench-29.4.1.tgz",
"integrity": "sha512-CbhGPgHX+b4AQKnxz/iziVHpgLG+eoGKIvIkOH+VmuOLxme7klbgvOpNB0Ab+XNq/u/AmOlKK5cd1dGuaN4iEA==",
"dev": true,
"requires": {
"@jest/globals": "^29.4.1",
"@jest/reporters": "^29.4.1",
"benchmark": "^2.1.4",
"chalk": "^4.1.0",
"lodash": "^4.17.20",
"ndjson": "^2.0.0"
}
},
"jest-changed-files": { "jest-changed-files": {
"version": "29.7.0", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
@@ -10748,6 +10907,12 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true "dev": true
}, },
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true
},
"json5": { "json5": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -11167,6 +11332,12 @@
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true
},
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -11179,6 +11350,19 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"ndjson": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz",
"integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==",
"dev": true,
"requires": {
"json-stringify-safe": "^5.0.1",
"minimist": "^1.2.5",
"readable-stream": "^3.6.0",
"split2": "^3.0.0",
"through2": "^4.0.0"
}
},
"node-fetch": { "node-fetch": {
"version": "2.6.7", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -11439,6 +11623,12 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"platform": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
"dev": true
},
"prelude-ls": { "prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -11521,6 +11711,17 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true "dev": true
}, },
"readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"redent": { "redent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@@ -11632,6 +11833,12 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -11737,6 +11944,15 @@
"source-map": "^0.6.0" "source-map": "^0.6.0"
} }
}, },
"split2": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
"dev": true,
"requires": {
"readable-stream": "^3.0.0"
}
},
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -11761,6 +11977,15 @@
"internal-slot": "^1.0.4" "internal-slot": "^1.0.4"
} }
}, },
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"requires": {
"safe-buffer": "~5.2.0"
}
},
"string-argv": { "string-argv": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
@@ -11862,6 +12087,15 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true "dev": true
}, },
"through2": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
"dev": true,
"requires": {
"readable-stream": "3"
}
},
"tmpl": { "tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -11977,6 +12211,12 @@
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
} }
}, },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"uuid": { "uuid": {
"version": "8.3.2", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+3 -1
View File
@@ -30,7 +30,8 @@
"format": "prettier --write .", "format": "prettier --write .",
"format:check": "prettier --check .", "format:check": "prettier --check .",
"prepare": "husky install", "prepare": "husky install",
"lint": "npx eslint --max-warnings 0 \"./src/**/*.js\" \"./scripts/**/*.js\" \"./tests/**/*.js\" \"./api/**/*.js\" \"./themes/**/*.js\"" "lint": "npx eslint --max-warnings 0 \"./src/**/*.js\" \"./scripts/**/*.js\" \"./tests/**/*.js\" \"./api/**/*.js\" \"./themes/**/*.js\"",
"bench": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.bench.config.js"
}, },
"author": "Anurag Hazra", "author": "Anurag Hazra",
"license": "MIT", "license": "MIT",
@@ -47,6 +48,7 @@
"hjson": "^3.2.2", "hjson": "^3.2.2",
"husky": "^8.0.3", "husky": "^8.0.3",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-bench": "^29.4.1",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lint-staged": "^14.0.1", "lint-staged": "^14.0.1",
+4 -2
View File
@@ -123,6 +123,8 @@ Please visit [this link](https://give.do/fundraisers/stand-beside-the-victims-of
> [!IMPORTANT]\ > [!IMPORTANT]\
> Since the GitHub API only [allows 5k requests per hour per user account](https://docs.github.com/en/graphql/overview/resource-limitations), the public Vercel instance hosted on `https://github-readme-stats.vercel.app/api` could possibly hit the rate limiter (see [#1471](https://github.com/anuraghazra/github-readme-stats/issues/1471)). We use caching to prevent this from happening (see https://github.com/anuraghazra/github-readme-stats#common-options). You can turn off these rate limit protections by deploying [your own Vercel instance](#disable-rate-limit-protections). > Since the GitHub API only [allows 5k requests per hour per user account](https://docs.github.com/en/graphql/overview/resource-limitations), the public Vercel instance hosted on `https://github-readme-stats.vercel.app/api` could possibly hit the rate limiter (see [#1471](https://github.com/anuraghazra/github-readme-stats/issues/1471)). We use caching to prevent this from happening (see https://github.com/anuraghazra/github-readme-stats#common-options). You can turn off these rate limit protections by deploying [your own Vercel instance](#disable-rate-limit-protections).
<img alt="Uptime Badge" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fgithub-readme-stats-git-monitoring-github-readme-stats-team.vercel.app%2Fapi%2Fstatus%2Fup%3Ftype%3Dshields">
# GitHub Stats Card # GitHub Stats Card
Copy-paste this into your markdown content, and that is it. Simple! Copy-paste this into your markdown content, and that is it. Simple!
@@ -137,7 +139,7 @@ Change the `?username=` value to your GitHub username.
> By default, the stats card only shows statistics like stars, commits and pull requests from public repositories. To show private statistics on the stats card, you should [deploy your own instance](#deploy-on-your-own) using your own GitHub API token. > By default, the stats card only shows statistics like stars, commits and pull requests from public repositories. To show private statistics on the stats card, you should [deploy your own instance](#deploy-on-your-own) using your own GitHub API token.
> [!NOTE]\ > [!NOTE]\
> Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [src/calculateRank.js](/src/calculateRank.js). The circle around the rank shows 100 minus the global percentile. > Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [src/calculateRank.js](src/calculateRank.js). The circle around the rank shows 100 minus the global percentile.
### Hiding individual stats ### Hiding individual stats
@@ -745,7 +747,7 @@ Since the GitHub API only allows 5k requests per hour, my `https://github-readme
> Since [#58](https://github.com/anuraghazra/github-readme-stats/pull/58), we should be able to handle more than 5k requests and have fewer issues with downtime :grin:. > Since [#58](https://github.com/anuraghazra/github-readme-stats/pull/58), we should be able to handle more than 5k requests and have fewer issues with downtime :grin:.
> [!NOTE]\ > [!NOTE]\
> If you are on the [Pro (i.e. paid)](https://vercel.com/pricing) Vercel plan, the [maxDuration](https://vercel.com/docs/concepts/projects/project-configuration#value-definition) value found in the [Vercel.json](/vercel.json) can be increased when your Vercel instance frequently times out during the card request. You are advised to keep this value lower than `30` seconds to prevent high memory usage. > If you are on the [Pro (i.e. paid)](https://vercel.com/pricing) Vercel plan, the [maxDuration](https://vercel.com/docs/concepts/projects/project-configuration#value-definition) value found in the [Vercel.json](vercel.json) can be increased when your Vercel instance frequently times out during the card request. You are advised to keep this value lower than `30` seconds to prevent high memory usage.
[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats) [![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/anuraghazra/github-readme-stats)
+1 -1
View File
@@ -14,7 +14,7 @@ const THEME_TEMPLATE = `## Available Themes
With inbuilt themes, you can customize the look of the card without doing any manual customization. With inbuilt themes, you can customize the look of the card without doing any manual customization.
Use \`?theme=THEME_NAME\` parameter like so :- Use \`?theme=THEME_NAME\` parameter like so:
\`\`\`md \`\`\`md
![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&theme=dark&show_icons=true) ![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&theme=dark&show_icons=true)
+46 -37
View File
@@ -4,6 +4,41 @@ import toEmoji from "emoji-name-map";
import wrap from "word-wrap"; import wrap from "word-wrap";
import { themes } from "../../themes/index.js"; import { themes } from "../../themes/index.js";
const TRY_AGAIN_LATER = "Please try again later";
const SECONDARY_ERROR_MESSAGES = {
MAX_RETRY:
"You can deploy own instance or wait until public will be no longer limited",
NO_TOKENS:
"Please add an env variable called PAT_1 with your GitHub API token in vercel",
USER_NOT_FOUND: "Make sure the provided username is not an organization",
GRAPHQL_ERROR: TRY_AGAIN_LATER,
GITHUB_REST_API_ERROR: TRY_AGAIN_LATER,
WAKATIME_USER_NOT_FOUND: "Make sure you have a public WakaTime profile",
};
/**
* Custom error class to handle custom GRS errors.
*/
class CustomError extends Error {
/**
* @param {string} message Error message.
* @param {string} type Error type.
*/
constructor(message, type) {
super(message);
this.type = type;
this.secondaryMessage = SECONDARY_ERROR_MESSAGES[type] || type;
}
static MAX_RETRY = "MAX_RETRY";
static NO_TOKENS = "NO_TOKENS";
static USER_NOT_FOUND = "USER_NOT_FOUND";
static GRAPHQL_ERROR = "GRAPHQL_ERROR";
static GITHUB_REST_API_ERROR = "GITHUB_REST_API_ERROR";
static WAKATIME_ERROR = "WAKATIME_ERROR";
}
// Script parameters. // Script parameters.
const ERROR_CARD_LENGTH = 576.5; const ERROR_CARD_LENGTH = 576.5;
@@ -23,6 +58,11 @@ const encodeHTML = (str) => {
.replace(/\u0008/gim, ""); .replace(/\u0008/gim, "");
}; };
const UPSTREAM_API_ERRORS = [
TRY_AGAIN_LATER,
SECONDARY_ERROR_MESSAGES.MAX_RETRY,
];
/** /**
* Renders error message on the card. * Renders error message on the card.
* *
@@ -41,7 +81,11 @@ const renderError = (message, secondaryMessage = "") => {
<rect x="0.5" y="0.5" width="${ <rect x="0.5" y="0.5" width="${
ERROR_CARD_LENGTH - 1 ERROR_CARD_LENGTH - 1
}" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/> }" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
<text x="25" y="45" class="text">Something went wrong! file an issue at https://tiny.one/readme-stats</text> <text x="25" y="45" class="text">Something went wrong!${
UPSTREAM_API_ERRORS.includes(secondaryMessage)
? ""
: " file an issue at https://tiny.one/readme-stats"
}</text>
<text data-testid="message" x="25" y="55" class="text small"> <text data-testid="message" x="25" y="55" class="text small">
<tspan x="25" dy="18">${encodeHTML(message)}</tspan> <tspan x="25" dy="18">${encodeHTML(message)}</tspan>
<tspan x="25" dy="18" class="gray">${secondaryMessage}</tspan> <tspan x="25" dy="18" class="gray">${secondaryMessage}</tspan>
@@ -188,7 +232,7 @@ const parseArray = (str) => {
*/ */
const clampValue = (number, min, max) => { const clampValue = (number, min, max) => {
// @ts-ignore // @ts-ignore
if (Number.isNaN(parseInt(number))) { if (Number.isNaN(parseInt(number, 10))) {
return min; return min;
} }
return Math.max(min, Math.min(number, max)); return Math.max(min, Math.min(number, max));
@@ -399,41 +443,6 @@ const CONSTANTS = {
ERROR_CACHE_SECONDS: TEN_MINUTES, ERROR_CACHE_SECONDS: TEN_MINUTES,
}; };
const TRY_AGAIN_LATER = "Please try again later";
const SECONDARY_ERROR_MESSAGES = {
MAX_RETRY:
"You can deploy own instance or wait until public will be no longer limited",
NO_TOKENS:
"Please add an env variable called PAT_1 with your GitHub API token in vercel",
USER_NOT_FOUND: "Make sure the provided username is not an organization",
GRAPHQL_ERROR: TRY_AGAIN_LATER,
GITHUB_REST_API_ERROR: TRY_AGAIN_LATER,
WAKATIME_USER_NOT_FOUND: "Make sure you have a public WakaTime profile",
};
/**
* Custom error class to handle custom GRS errors.
*/
class CustomError extends Error {
/**
* @param {string} message Error message.
* @param {string} type Error type.
*/
constructor(message, type) {
super(message);
this.type = type;
this.secondaryMessage = SECONDARY_ERROR_MESSAGES[type] || type;
}
static MAX_RETRY = "MAX_RETRY";
static NO_TOKENS = "NO_TOKENS";
static USER_NOT_FOUND = "USER_NOT_FOUND";
static GRAPHQL_ERROR = "GRAPHQL_ERROR";
static GITHUB_REST_API_ERROR = "GITHUB_REST_API_ERROR";
static WAKATIME_ERROR = "WAKATIME_ERROR";
}
/** /**
* Missing query parameter class. * Missing query parameter class.
*/ */
+47 -13
View File
@@ -40,7 +40,7 @@ const GRAPHQL_REPOS_QUERY = `
`; `;
const GRAPHQL_STATS_QUERY = ` const GRAPHQL_STATS_QUERY = `
query userInfo($login: String!, $after: String) { query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!) {
user(login: $login) { user(login: $login) {
name name
login login
@@ -54,7 +54,7 @@ const GRAPHQL_STATS_QUERY = `
pullRequests(first: 1) { pullRequests(first: 1) {
totalCount totalCount
} }
mergedPullRequests: pullRequests(states: MERGED) { mergedPullRequests: pullRequests(states: MERGED) @include(if: $includeMergedPullRequests) {
totalCount totalCount
} }
openIssues: issues(states: OPEN) { openIssues: issues(states: OPEN) {
@@ -66,10 +66,10 @@ const GRAPHQL_STATS_QUERY = `
followers { followers {
totalCount totalCount
} }
repositoryDiscussions { repositoryDiscussions @include(if: $includeDiscussions) {
totalCount totalCount
} }
repositoryDiscussionComments(onlyAnswers: true) { repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {
totalCount totalCount
} }
${GRAPHQL_REPOS_FIELD} ${GRAPHQL_REPOS_FIELD}
@@ -104,17 +104,33 @@ const fetcher = (variables, token) => {
/** /**
* Fetch stats information for a given username. * Fetch stats information for a given username.
* *
* @param {string} username Github username. * @param {object} variables Fetcher variables.
* @param {string} variables.username Github username.
* @param {boolean} variables.includeMergedPullRequests Include merged pull requests.
* @param {boolean} variables.includeDiscussions Include discussions.
* @param {boolean} variables.includeDiscussionsAnswers Include discussions answers.
* @returns {Promise<AxiosResponse>} Axios response. * @returns {Promise<AxiosResponse>} Axios response.
* *
* @description This function supports multi-page fetching if the 'FETCH_MULTI_PAGE_STARS' environment variable is set to true. * @description This function supports multi-page fetching if the 'FETCH_MULTI_PAGE_STARS' environment variable is set to true.
*/ */
const statsFetcher = async (username) => { const statsFetcher = async ({
username,
includeMergedPullRequests,
includeDiscussions,
includeDiscussionsAnswers,
}) => {
let stats; let stats;
let hasNextPage = true; let hasNextPage = true;
let endCursor = null; let endCursor = null;
while (hasNextPage) { while (hasNextPage) {
const variables = { login: username, first: 100, after: endCursor }; const variables = {
login: username,
first: 100,
after: endCursor,
includeMergedPullRequests,
includeDiscussions,
includeDiscussionsAnswers,
};
let res = await retryer(fetcher, variables); let res = await retryer(fetcher, variables);
if (res.data.errors) { if (res.data.errors) {
return res; return res;
@@ -198,12 +214,18 @@ const totalCommitsFetcher = async (username) => {
* @param {string} username GitHub username. * @param {string} username GitHub username.
* @param {boolean} include_all_commits Include all commits. * @param {boolean} include_all_commits Include all commits.
* @param {string[]} exclude_repo Repositories to exclude. * @param {string[]} exclude_repo Repositories to exclude.
* @param {boolean} include_merged_pull_requests Include merged pull requests.
* @param {boolean} include_discussions Include discussions.
* @param {boolean} include_discussions_answers Include discussions answers.
* @returns {Promise<StatsData>} Stats data. * @returns {Promise<StatsData>} Stats data.
*/ */
const fetchStats = async ( const fetchStats = async (
username, username,
include_all_commits = false, include_all_commits = false,
exclude_repo = [], exclude_repo = [],
include_merged_pull_requests = false,
include_discussions = false,
include_discussions_answers = false,
) => { ) => {
if (!username) { if (!username) {
throw new MissingParamError(["username"]); throw new MissingParamError(["username"]);
@@ -224,7 +246,12 @@ const fetchStats = async (
rank: { level: "C", percentile: 100 }, rank: { level: "C", percentile: 100 },
}; };
let res = await statsFetcher(username); let res = await statsFetcher({
username,
includeMergedPullRequests: include_merged_pull_requests,
includeDiscussions: include_discussions,
includeDiscussionsAnswers: include_discussions_answers,
});
// Catch GraphQL errors. // Catch GraphQL errors.
if (res.data.errors) { if (res.data.errors) {
@@ -259,14 +286,21 @@ const fetchStats = async (
} }
stats.totalPRs = user.pullRequests.totalCount; stats.totalPRs = user.pullRequests.totalCount;
stats.totalPRsMerged = user.mergedPullRequests.totalCount; if (include_merged_pull_requests) {
stats.mergedPRsPercentage = stats.totalPRsMerged = user.mergedPullRequests.totalCount;
(user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100; stats.mergedPRsPercentage =
(user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100;
}
stats.totalReviews = stats.totalReviews =
user.contributionsCollection.totalPullRequestReviewContributions; user.contributionsCollection.totalPullRequestReviewContributions;
stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount; stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount;
stats.totalDiscussionsStarted = user.repositoryDiscussions.totalCount; if (include_discussions) {
stats.totalDiscussionsAnswered = user.repositoryDiscussionComments.totalCount; stats.totalDiscussionsStarted = user.repositoryDiscussions.totalCount;
}
if (include_discussions_answers) {
stats.totalDiscussionsAnswered =
user.repositoryDiscussionComments.totalCount;
}
stats.contributedTo = user.repositoriesContributedTo.totalCount; stats.contributedTo = user.repositoriesContributedTo.totalCount;
// Retrieve stars while filtering out repositories to be hidden. // Retrieve stars while filtering out repositories to be hidden.
+4
View File
@@ -321,5 +321,9 @@ describe("Test /api/", () => {
expect(res.send).toBeCalledWith( expect(res.send).toBeCalledWith(
renderError("Could not fetch total commits.", "Please try again later"), renderError("Could not fetch total commits.", "Please try again later"),
); );
// Received SVG output should not contain string "https://tiny.one/readme-stats"
expect(res.send.mock.calls[0][0]).not.toContain(
"https://tiny.one/readme-stats",
);
}); });
}); });
+76
View File
@@ -0,0 +1,76 @@
import { benchmarkSuite } from "jest-bench";
import api from "../../api/index.js";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { jest } from "@jest/globals";
const stats = {
name: "Anurag Hazra",
totalStars: 100,
totalCommits: 200,
totalIssues: 300,
totalPRs: 400,
totalPRsMerged: 320,
mergedPRsPercentage: 80,
totalReviews: 50,
totalDiscussionsStarted: 10,
totalDiscussionsAnswered: 40,
contributedTo: 50,
rank: null,
};
const data_stats = {
data: {
user: {
name: stats.name,
repositoriesContributedTo: { totalCount: stats.contributedTo },
contributionsCollection: {
totalCommitContributions: stats.totalCommits,
totalPullRequestReviewContributions: stats.totalReviews,
},
pullRequests: { totalCount: stats.totalPRs },
mergedPullRequests: { totalCount: stats.totalPRsMerged },
openIssues: { totalCount: stats.totalIssues },
closedIssues: { totalCount: 0 },
followers: { totalCount: 0 },
repositoryDiscussions: { totalCount: stats.totalDiscussionsStarted },
repositoryDiscussionComments: {
totalCount: stats.totalDiscussionsAnswered,
},
repositories: {
totalCount: 1,
nodes: [{ stargazers: { totalCount: 100 } }],
pageInfo: {
hasNextPage: false,
endCursor: "cursor",
},
},
},
},
};
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").replyOnce(200, data);
return { req, res };
};
benchmarkSuite("test /api", {
["simple request"]: async () => {
const { req, res } = faker({}, data_stats);
await api(req, res);
},
});
+17
View File
@@ -0,0 +1,17 @@
import { benchmarkSuite } from "jest-bench";
import { calculateRank } from "../../src/calculateRank.js";
benchmarkSuite("calculateRank", {
["calculateRank"]: () => {
calculateRank({
all_commits: false,
commits: 1300,
prs: 1500,
issues: 4500,
reviews: 1000,
repos: 0,
stars: 600000,
followers: 50000,
});
},
});
+82 -24
View File
@@ -122,12 +122,12 @@ describe("Test fetchStats", () => {
totalCommits: 100, totalCommits: 100,
totalIssues: 200, totalIssues: 200,
totalPRs: 300, totalPRs: 300,
totalPRsMerged: 240, totalPRsMerged: 0,
mergedPRsPercentage: 80, mergedPRsPercentage: 0,
totalReviews: 50, totalReviews: 50,
totalStars: 300, totalStars: 300,
totalDiscussionsStarted: 10, totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 40, totalDiscussionsAnswered: 0,
rank, rank,
}); });
}); });
@@ -158,12 +158,12 @@ describe("Test fetchStats", () => {
totalCommits: 100, totalCommits: 100,
totalIssues: 200, totalIssues: 200,
totalPRs: 300, totalPRs: 300,
totalPRsMerged: 240, totalPRsMerged: 0,
mergedPRsPercentage: 80, mergedPRsPercentage: 0,
totalReviews: 50, totalReviews: 50,
totalStars: 300, totalStars: 300,
totalDiscussionsStarted: 10, totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 40, totalDiscussionsAnswered: 0,
rank, rank,
}); });
}); });
@@ -200,12 +200,12 @@ describe("Test fetchStats", () => {
totalCommits: 1000, totalCommits: 1000,
totalIssues: 200, totalIssues: 200,
totalPRs: 300, totalPRs: 300,
totalPRsMerged: 240, totalPRsMerged: 0,
mergedPRsPercentage: 80, mergedPRsPercentage: 0,
totalReviews: 50, totalReviews: 50,
totalStars: 300, totalStars: 300,
totalDiscussionsStarted: 10, totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 40, totalDiscussionsAnswered: 0,
rank, rank,
}); });
}); });
@@ -249,12 +249,12 @@ describe("Test fetchStats", () => {
totalCommits: 1000, totalCommits: 1000,
totalIssues: 200, totalIssues: 200,
totalPRs: 300, totalPRs: 300,
totalPRsMerged: 240, totalPRsMerged: 0,
mergedPRsPercentage: 80, mergedPRsPercentage: 0,
totalReviews: 50, totalReviews: 50,
totalStars: 200, totalStars: 200,
totalDiscussionsStarted: 10, totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 40, totalDiscussionsAnswered: 0,
rank, rank,
}); });
}); });
@@ -280,12 +280,12 @@ describe("Test fetchStats", () => {
totalCommits: 100, totalCommits: 100,
totalIssues: 200, totalIssues: 200,
totalPRs: 300, totalPRs: 300,
totalPRsMerged: 240, totalPRsMerged: 0,
mergedPRsPercentage: 80, mergedPRsPercentage: 0,
totalReviews: 50, totalReviews: 50,
totalStars: 400, totalStars: 400,
totalDiscussionsStarted: 10, totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 40, totalDiscussionsAnswered: 0,
rank, rank,
}); });
}); });
@@ -311,12 +311,12 @@ describe("Test fetchStats", () => {
totalCommits: 100, totalCommits: 100,
totalIssues: 200, totalIssues: 200,
totalPRs: 300, totalPRs: 300,
totalPRsMerged: 240, totalPRsMerged: 0,
mergedPRsPercentage: 80, mergedPRsPercentage: 0,
totalReviews: 50, totalReviews: 50,
totalStars: 300, totalStars: 300,
totalDiscussionsStarted: 10, totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 40, totalDiscussionsAnswered: 0,
rank, rank,
}); });
}); });
@@ -336,6 +336,64 @@ describe("Test fetchStats", () => {
followers: 100, followers: 100,
}); });
expect(stats).toStrictEqual({
contributedTo: 61,
name: "Anurag Hazra",
totalCommits: 100,
totalIssues: 200,
totalPRs: 300,
totalPRsMerged: 0,
mergedPRsPercentage: 0,
totalReviews: 50,
totalStars: 300,
totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 0,
rank,
});
});
it("should not fetch additional stats data when it not requested", async () => {
let stats = await fetchStats("anuraghazra");
const rank = calculateRank({
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,
followers: 100,
});
expect(stats).toStrictEqual({
contributedTo: 61,
name: "Anurag Hazra",
totalCommits: 100,
totalIssues: 200,
totalPRs: 300,
totalPRsMerged: 0,
mergedPRsPercentage: 0,
totalReviews: 50,
totalStars: 300,
totalDiscussionsStarted: 0,
totalDiscussionsAnswered: 0,
rank,
});
});
it("should fetch additional stats when it requested", async () => {
let stats = await fetchStats("anuraghazra", false, [], true, true, true);
const rank = calculateRank({
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,
followers: 100,
});
expect(stats).toStrictEqual({ expect(stats).toStrictEqual({
contributedTo: 61, contributedTo: 61,
name: "Anurag Hazra", name: "Anurag Hazra",