From 650ae5ae81109c2ef11c7e0f9a024e7934aca44f Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sat, 9 Dec 2023 06:52:03 +0700 Subject: [PATCH 01/33] chore: update npm ignore files --- .npmignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.npmignore b/.npmignore index 483dbe0..457d2f8 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1,6 @@ +.env.example .github/ +.tests/ scripts/ +coverage/ +public/ From 0e0fe562c1706b30fc03b206a6b4ce5a9bee65ad Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sat, 9 Dec 2023 08:18:06 +0700 Subject: [PATCH 02/33] docs(readme): Added badge join to community (#207) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24b48cd..67a6353 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ GitHub Readme Profile Issues GitHub Readme Profile PRs + Join to Community
@@ -370,4 +371,4 @@ Want to contribute? Please note our contribution guidelines [here](/CONTRIBUTING

Copyright © 2023 Rangga Fajar Oktariansyah

GitHub Readme Profile License -
\ No newline at end of file + From ecb3bfd4829f0df6ec2fb86167552eea187b257c Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sat, 9 Dec 2023 20:28:45 +0700 Subject: [PATCH 03/33] docs(readme): fixed small typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67a6353..30f5de7 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@

Report Bugs · Request Feature · Ask Question

GitHub Readme Profile Version GitHub Readme Profile Code Quality - GitHub Readme Profile Issues - GitHub Readme Profile PRs + GitHub Readme Profile Issues + GitHub Readme Profile PRs Join to Community From e00e8d4d28716c57861bc7bee250c0e8fadee5ab Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sun, 10 Dec 2023 12:38:24 +0700 Subject: [PATCH 04/33] ci(generate-locale-readme): fixed message commit --- .github/workflows/generate-locale-doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-locale-doc.yml b/.github/workflows/generate-locale-doc.yml index 30be507..ba2f7e8 100644 --- a/.github/workflows/generate-locale-doc.yml +++ b/.github/workflows/generate-locale-doc.yml @@ -58,7 +58,7 @@ jobs: - name: Push commit to a new branch and create prs run: | branch="auto_update_locale_readme" - message="docs(translation): auto update translation readme" + message="docs(i18n): auto update translation readme" body=$(printf "## Changes File\n\n\`\`\`diff\n$(git diff)\n\`\`\`\n\n> Co-authored-by: github-actions[bot] ") if [[ "$(git status --porcelain)" != "" ]]; then git branch -D ${branch} || true From b273770aef5f35b7d52d1eafb72c70495a1acd2d Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sun, 10 Dec 2023 12:43:07 +0700 Subject: [PATCH 05/33] infra(mergify): added automatic reviews for github actions is actor --- .github/mergify.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/mergify.yml b/.github/mergify.yml index ebb96c8..b8a4c77 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -1,8 +1,6 @@ queue_rules: - # If you have other queues defined, add this at the end so it is processed last - name: dep-update batch_size: 10 - # Wait for up to 30 minutes for the batch to fill up batch_max_wait_time: 30 min queue_conditions: - author = dependabot[bot] @@ -20,8 +18,15 @@ pull_request_rules: - "#approved-reviews-by>=1" actions: queue: + - name: Automatic approve for GitHub Actions pull requests + conditions: + - author = github-actions[bot] + actions: + review: + type: APPROVE - name: Automatic merge for GitHub Actions pull requests conditions: - author = github-actions[bot] + - "#approved-reviews-by>=1" actions: merge: From b9187910dfb199d13a2837fdcdf9a168157232f8 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Tue, 12 Dec 2023 09:14:32 +0700 Subject: [PATCH 06/33] chore: fixed small typo --- .npmignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index 457d2f8..a5cfb4a 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,6 @@ .env.example .github/ -.tests/ +tests/ scripts/ coverage/ public/ From 55281f3360cab0477fd26344700fa63cc7eee47e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Dec 2023 01:20:19 +0000 Subject: [PATCH 07/33] build(deps): bump all dependencies and devDependencies version (#208) Co-authored-by: github-actions[bot] --- package-lock.json | 86 +++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9dc5ad7..75f32a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -156,21 +156,21 @@ } }, "node_modules/@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", + "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", + "@babel/helpers": "^7.23.6", + "@babel/parser": "^7.23.6", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -209,12 +209,12 @@ "dev": true }, "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -224,14 +224,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -365,14 +365,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", + "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" @@ -464,9 +464,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -667,20 +667,20 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -711,9 +711,9 @@ "dev": true }, "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -1996,9 +1996,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001566", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", - "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", + "version": "1.0.30001570", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", + "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", "dev": true, "funding": [ { @@ -2342,9 +2342,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.609", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.609.tgz", - "integrity": "sha512-ihiCP7PJmjoGNuLpl7TjNA8pCQWu09vGyjlPYw1Rqww4gvNuCcmvl+44G+2QyJ6S2K4o+wbTS++Xz0YN8Q9ERw==", + "version": "1.4.613", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.613.tgz", + "integrity": "sha512-r4x5+FowKG6q+/Wj0W9nidx7QO31BJwmR2uEo+Qh3YLGQ8SbBAFuDFpTxzly/I2gsbrFwBuIjrMp423L3O5U3w==", "dev": true }, "node_modules/emittery": { From 303b119285687922e99634ec872d02df333ff3e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:41:00 +0000 Subject: [PATCH 08/33] build(deps-dev): bump @types/node from 20.10.4 to 20.10.5 (#209) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.4 to 20.10.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75f32a5..8bd8a4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1549,9 +1549,9 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "version": "20.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", + "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", "dependencies": { "undici-types": "~5.26.4" } From aed99a66842b165cd72189f6f3c9c09734fef001 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:41:05 +0000 Subject: [PATCH 09/33] build(deps): bump github/codeql-action from 2 to 3 (#210) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fda6093..f9daa60 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -64,7 +64,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -77,6 +77,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" From 94a1a0e5599249454833ff16532db0d9276ccb0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:41:10 +0000 Subject: [PATCH 10/33] build(deps): bump actions/setup-node from 4.0.0 to 4.0.1 (#211) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4...v4.0.1) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/auto-build-pkg.yml | 2 +- .github/workflows/generate-locale-doc.yml | 2 +- .github/workflows/generate-theme-doc.yml | 2 +- .github/workflows/setup-package.yml | 2 +- .github/workflows/test.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/auto-build-pkg.yml b/.github/workflows/auto-build-pkg.yml index e970af8..0f13498 100644 --- a/.github/workflows/auto-build-pkg.yml +++ b/.github/workflows/auto-build-pkg.yml @@ -39,7 +39,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} cache: npm diff --git a/.github/workflows/generate-locale-doc.yml b/.github/workflows/generate-locale-doc.yml index ba2f7e8..4beefe3 100644 --- a/.github/workflows/generate-locale-doc.yml +++ b/.github/workflows/generate-locale-doc.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} cache: npm diff --git a/.github/workflows/generate-theme-doc.yml b/.github/workflows/generate-theme-doc.yml index b99faea..0474451 100644 --- a/.github/workflows/generate-theme-doc.yml +++ b/.github/workflows/generate-theme-doc.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} cache: npm diff --git a/.github/workflows/setup-package.yml b/.github/workflows/setup-package.yml index f09849b..fb65ce1 100644 --- a/.github/workflows/setup-package.yml +++ b/.github/workflows/setup-package.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v4 # Setup .npmrc file to publish to GitHub Packages - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v4.0.1 with: node-version: '20.x' registry-url: 'https://npm.pkg.github.com' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a4f2db6..ca11478 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} cache: npm From e4d10baf8ffedb78b5315fc5c2accf4017d58c97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:41:16 +0000 Subject: [PATCH 11/33] build(deps): bump rickstaa/top-issues-action from 1.3.77 to 1.3.83 (#212) Bumps [rickstaa/top-issues-action](https://github.com/rickstaa/top-issues-action) from 1.3.77 to 1.3.83. - [Release notes](https://github.com/rickstaa/top-issues-action/releases) - [Commits](https://github.com/rickstaa/top-issues-action/compare/8c70898862d1e7808bd5ce009405ac44f461044d...ce1a949c62a7a5d8b4890d596b5c5da37db628ca) --- updated-dependencies: - dependency-name: rickstaa/top-issues-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/top-issues-dashboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/top-issues-dashboard.yml b/.github/workflows/top-issues-dashboard.yml index 9bdf214..b9c05e3 100644 --- a/.github/workflows/top-issues-dashboard.yml +++ b/.github/workflows/top-issues-dashboard.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Run top issues action - uses: rickstaa/top-issues-action@8c70898862d1e7808bd5ce009405ac44f461044d # v1.3.77 + uses: rickstaa/top-issues-action@ce1a949c62a7a5d8b4890d596b5c5da37db628ca # v1.3.83 env: github_token: ${{ secrets.GITHUB_TOKEN }} with: From dcb0f8dedcd869bec81dd94c93e74ef6b8a2b3c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 01:17:16 +0000 Subject: [PATCH 12/33] build(deps): bump all dependencies and devDependencies version (#214) Co-authored-by: github-actions[bot] --- package-lock.json | 26 +++++++++++++------------- package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8bd8a4c..30fac6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "devDependencies": { "@markdoc/markdoc": "^0.4.0", "@types/jest": "^29.5.11", - "@types/node": "^20.10.4", + "@types/node": "^20.10.5", "jest": "^29.7.0", "nodemon": "^3.0.2", "ts-jest": "^29.1.1", @@ -1399,9 +1399,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" @@ -1557,9 +1557,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -1996,9 +1996,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "version": "1.0.30001571", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz", + "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==", "dev": true, "funding": [ { @@ -2342,9 +2342,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.613", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.613.tgz", - "integrity": "sha512-r4x5+FowKG6q+/Wj0W9nidx7QO31BJwmR2uEo+Qh3YLGQ8SbBAFuDFpTxzly/I2gsbrFwBuIjrMp423L3O5U3w==", + "version": "1.4.616", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", + "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==", "dev": true }, "node_modules/emittery": { diff --git a/package.json b/package.json index f72bc1b..6e31aa5 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "devDependencies": { "@markdoc/markdoc": "^0.4.0", "@types/jest": "^29.5.11", - "@types/node": "^20.10.4", + "@types/node": "^20.10.5", "jest": "^29.7.0", "nodemon": "^3.0.2", "ts-jest": "^29.1.1", From 14734470806f1ec49ebab77c57465520c9098b66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 20:31:25 +0000 Subject: [PATCH 13/33] build(deps): bump rickstaa/top-issues-action from 1.3.83 to 1.3.86 (#215) Bumps [rickstaa/top-issues-action](https://github.com/rickstaa/top-issues-action) from 1.3.83 to 1.3.86. - [Release notes](https://github.com/rickstaa/top-issues-action/releases) - [Commits](https://github.com/rickstaa/top-issues-action/compare/ce1a949c62a7a5d8b4890d596b5c5da37db628ca...c89f81ea20ab265071194cf102bc881f476ecca8) --- updated-dependencies: - dependency-name: rickstaa/top-issues-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/top-issues-dashboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/top-issues-dashboard.yml b/.github/workflows/top-issues-dashboard.yml index b9c05e3..129ea6b 100644 --- a/.github/workflows/top-issues-dashboard.yml +++ b/.github/workflows/top-issues-dashboard.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Run top issues action - uses: rickstaa/top-issues-action@ce1a949c62a7a5d8b4890d596b5c5da37db628ca # v1.3.83 + uses: rickstaa/top-issues-action@c89f81ea20ab265071194cf102bc881f476ecca8 # v1.3.86 env: github_token: ${{ secrets.GITHUB_TOKEN }} with: From 6314772042d7cb2b6efefa4025fc3f87e040d952 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Dec 2023 01:08:08 +0000 Subject: [PATCH 14/33] build(deps): bump all dependencies and devDependencies version (#216) Co-authored-by: github-actions[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30fac6d..5ce96db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@resvg/resvg-js": "^2.6.0", "@types/escape-html": "^1.0.4", "@types/express": "^4.17.21", - "axios": "^1.6.2", + "axios": "^1.6.3", "dotenv": "^16.3.1", "escape-html": "^1.0.3", "express": "^4.18.2", @@ -1721,9 +1721,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", + "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -1996,9 +1996,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001571", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz", - "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==", + "version": "1.0.30001572", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", + "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index 6e31aa5..5c16f78 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@resvg/resvg-js": "^2.6.0", "@types/escape-html": "^1.0.4", "@types/express": "^4.17.21", - "axios": "^1.6.2", + "axios": "^1.6.3", "dotenv": "^16.3.1", "escape-html": "^1.0.3", "express": "^4.18.2", From 972dd96548a77b39de6115a562eb6f689c8a8b53 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 01:25:16 +0000 Subject: [PATCH 15/33] build(deps): bump all dependencies and devDependencies version (#217) Co-authored-by: github-actions[bot] --- package-lock.json | 50 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ce96db..a141ecc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "devDependencies": { "@markdoc/markdoc": "^0.4.0", "@types/jest": "^29.5.11", - "@types/node": "^20.10.5", + "@types/node": "^20.10.6", "jest": "^29.7.0", "nodemon": "^3.0.2", "ts-jest": "^29.1.1", @@ -156,9 +156,9 @@ } }, "node_modules/@babel/core": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", - "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -166,10 +166,10 @@ "@babel/generator": "^7.23.6", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.6", + "@babel/helpers": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", + "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -365,13 +365,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", - "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", + "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", + "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" }, "engines": { @@ -667,9 +667,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", @@ -1418,9 +1418,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" @@ -1549,9 +1549,9 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { - "version": "20.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", - "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", "dependencies": { "undici-types": "~5.26.4" } @@ -1625,9 +1625,9 @@ } }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2571,9 +2571,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", diff --git a/package.json b/package.json index 5c16f78..a3cb484 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "devDependencies": { "@markdoc/markdoc": "^0.4.0", "@types/jest": "^29.5.11", - "@types/node": "^20.10.5", + "@types/node": "^20.10.6", "jest": "^29.7.0", "nodemon": "^3.0.2", "ts-jest": "^29.1.1", From b87b3924a3abd55758a93c4c6a2dc32f94b955ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 19:38:38 +0000 Subject: [PATCH 16/33] build(deps): bump rickstaa/top-issues-action from 1.3.86 to 1.3.87 (#218) Bumps [rickstaa/top-issues-action](https://github.com/rickstaa/top-issues-action) from 1.3.86 to 1.3.87. - [Release notes](https://github.com/rickstaa/top-issues-action/releases) - [Commits](https://github.com/rickstaa/top-issues-action/compare/c89f81ea20ab265071194cf102bc881f476ecca8...fa1b14384871ebbc2f341f953a728e8370788992) --- updated-dependencies: - dependency-name: rickstaa/top-issues-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/top-issues-dashboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/top-issues-dashboard.yml b/.github/workflows/top-issues-dashboard.yml index 129ea6b..b679134 100644 --- a/.github/workflows/top-issues-dashboard.yml +++ b/.github/workflows/top-issues-dashboard.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Run top issues action - uses: rickstaa/top-issues-action@c89f81ea20ab265071194cf102bc881f476ecca8 # v1.3.86 + uses: rickstaa/top-issues-action@fa1b14384871ebbc2f341f953a728e8370788992 # v1.3.87 env: github_token: ${{ secrets.GITHUB_TOKEN }} with: From 4038ca837710621c7f39a04f777d98a58693d0c9 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:59:29 +0700 Subject: [PATCH 17/33] refactor: revise code for readability and maintainability (#219) --- src/card.ts | 277 ++++++++++++++++++++++++++-------------------------- 1 file changed, 138 insertions(+), 139 deletions(-) diff --git a/src/card.ts b/src/card.ts index f126e63..935ec8f 100644 --- a/src/card.ts +++ b/src/card.ts @@ -24,64 +24,67 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { // Setting angles and positions for SVG elements const direction = isRtlDirection ? "rtl" : "ltr"; - const titleXAngle = isDisabledAnimations ? (isRtlDirection ? 520 : 15) : (isRtlDirection ? 510 : 5); - const titleYAngle = isDisabledAnimations ? 0 : -10; - const textXAngle = isRtlDirection ? 215 : 25; - const dataXAngle = isRtlDirection ? 15 : 225; - const iconXAngle = isRtlDirection ? 225 : 0; - const imageXAngle = isDisabledAnimations ? 120 : 125; - const imageYAngle = isDisabledAnimations ? 70 : 65; - const userXAngle = isDisabledAnimations ? 119.9 : 109.9; - const userYAngle = isDisabledAnimations ? 140 : 130; - const follXAngle = isDisabledAnimations ? 120 : 110; - const follYAngle = isDisabledAnimations ? 161 : 151; + const angle = { + titleXAngle: isDisabledAnimations ? (isRtlDirection ? 520 : 15) : (isRtlDirection ? 510 : 5), + titleYAngle: isDisabledAnimations ? 0 : -10, + textXAngle: isRtlDirection ? 210 : 25, + dataXAngle: isRtlDirection ? 5 : 230, + iconXAngle: isRtlDirection ? 220 : 0, + imageXAngle: isDisabledAnimations ? 120 : 125, + imageYAngle: isDisabledAnimations ? 70 : 65, + userXAngle: isDisabledAnimations ? 119.9 : 109.9, + userYAngle: isDisabledAnimations ? 140 : 130, + follXAngle: isDisabledAnimations ? 120 : 110, + follYAngle: isDisabledAnimations ? 161 : 151, + }; // Setting styles for hiding stroke and border based on UI configuration const hideStroke = parseBoolean(uiConfig.hideStroke) ? `` : `stroke="#${uiConfig.strokeColor}" stroke-width="5"`; const hideBorder = parseBoolean(uiConfig.hideBorder) ? `` : `stroke="#${uiConfig.borderColor}" stroke-opacity="1" stroke-width="${uiConfig.borderWidth}"`; // CSS animations for SVG elements - const animations = parseBoolean(uiConfig.disabledAnimations || uiConfig.Format === "png") ? `` : ` /* Animations */ - @keyframes scaleInAnimation { - from { - transform: translate(-5px, 5px) scale(0); - } - to { - transform: translate(-5px, 5px) scale(1); - } - } - @keyframes fadeInAnimation { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - @keyframes fadeLeftInAnimation { - from { - opacity: 0; - transform: translate(-90px, 10px); - } - to { - opacity: 1; - transform: translate(10px, 10px); - } - } - - .div-animation { - animation: fadeLeftInAnimation 0.7s ease-in-out forwards; - } - - .image-profile-animation { - animation: scaleInAnimation 1.2s ease-in-out forwards; - transform-origin: ${imageXAngle}px ${imageYAngle}px; - } - - .single-item-animation { - opacity: 0; - animation: fadeInAnimation 0.3s ease-in-out forwards; - }`; + const animations = parseBoolean(uiConfig.disabledAnimations || uiConfig.Format === "png") ? `` : ` + /* Animations */ + @keyframes scaleInAnimation { + from { + transform: translate(-5px, 5px) scale(0); + } + to { + transform: translate(-5px, 5px) scale(1); + } + } + @keyframes fadeInAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + @keyframes fadeLeftInAnimation { + from { + opacity: 0; + transform: translate(-90px, 10px); + } + to { + opacity: 1; + transform: translate(10px, 10px); + } + } + + .div-animation { + animation: fadeLeftInAnimation 0.7s ease-in-out forwards; + } + + .image-profile-animation { + animation: scaleInAnimation 1.2s ease-in-out forwards; + transform-origin: ${angle.imageXAngle}px ${angle.imageYAngle}px; + } + + .single-item-animation { + opacity: 0; + animation: fadeInAnimation 0.3s ease-in-out forwards; + }`; // Extracting and formatting hidden and shown items based on UI configuration const hiddenItems = uiConfig.hiddenItems || ""; @@ -110,15 +113,15 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { // Generating SVG code for each visible card item const cardItemsSVG = cardItemsToShow.map((item, index) => ` - - - - ${item.icon} - - ${item.text}: - ${item.value} - - `).join("\n"); + + + + ${item.icon} + + ${item.text}: + ${item.value} + + `).join("\n"); /** * Generates a linear gradient SVG code based on an array of color stops. @@ -135,13 +138,12 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { return ``; }).join(""); return ` - - - ${gradientStops} - - - - `; + + + ${gradientStops} + + + `; } // Generating SVG code for the background based on background color or gradient @@ -155,81 +157,78 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { const gradientColors = gradientHexArray.map(color => color.trim()); backgroundSVG = generateGradient(gradientColors); } else { - backgroundSVG = ``; + backgroundSVG = ` + `; } } } // Final SVG code for the GitHub stats card - return ` - - ${selectLocale.titleCard.split("{name}").join(data.name) || defaultLocale.titleCard.split("{name}").join(data.name)} - - ${backgroundSVG} - - - ${selectLocale.titleCard.split("{name}").join(data.name) || defaultLocale.titleCard.split("{name}").join(data.name)} - - - - - - - - - - @${data.username} - - ${data.followers} ${selectLocale.followersText || defaultLocale.followersText} · ${data.following} ${selectLocale.followingText || defaultLocale.followingText} - - - - ${cardItemsSVG} - + return ` + + + ${selectLocale.titleCard.split("{name}").join(data.name) || defaultLocale.titleCard.split("{name}").join(data.name)} +${backgroundSVG} + + + ${selectLocale.titleCard.split("{name}").join(data.name) || defaultLocale.titleCard.split("{name}").join(data.name)} + + + + + + + + + + @${data.username} + + ${data.followers} ${selectLocale.followersText || defaultLocale.followersText} · ${data.following} ${selectLocale.followingText || defaultLocale.followingText} +${cardItemsSVG} + `; -} \ No newline at end of file +} From 1f4d7a1b113b989ba54d2363418b89698336da79 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Wed, 3 Jan 2024 03:57:14 +0700 Subject: [PATCH 18/33] fix: update x position for data stats text (#220) --- src/card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/card.ts b/src/card.ts index 935ec8f..0df2d35 100644 --- a/src/card.ts +++ b/src/card.ts @@ -28,7 +28,7 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { titleXAngle: isDisabledAnimations ? (isRtlDirection ? 520 : 15) : (isRtlDirection ? 510 : 5), titleYAngle: isDisabledAnimations ? 0 : -10, textXAngle: isRtlDirection ? 210 : 25, - dataXAngle: isRtlDirection ? 5 : 230, + dataXAngle: isRtlDirection ? 15 : 220, iconXAngle: isRtlDirection ? 220 : 0, imageXAngle: isDisabledAnimations ? 120 : 125, imageYAngle: isDisabledAnimations ? 70 : 65, From c9a9f246e779ca0f6e907d67bcc38df916bb183a Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Wed, 3 Jan 2024 04:06:05 +0700 Subject: [PATCH 19/33] fix: update x position for text and icon (#221) --- src/card.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/card.ts b/src/card.ts index 0df2d35..efd1fca 100644 --- a/src/card.ts +++ b/src/card.ts @@ -27,9 +27,9 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { const angle = { titleXAngle: isDisabledAnimations ? (isRtlDirection ? 520 : 15) : (isRtlDirection ? 510 : 5), titleYAngle: isDisabledAnimations ? 0 : -10, - textXAngle: isRtlDirection ? 210 : 25, + textXAngle: isRtlDirection ? 205 : 20, dataXAngle: isRtlDirection ? 15 : 220, - iconXAngle: isRtlDirection ? 220 : 0, + iconXAngle: isRtlDirection ? 215 : -5, imageXAngle: isDisabledAnimations ? 120 : 125, imageYAngle: isDisabledAnimations ? 70 : 65, userXAngle: isDisabledAnimations ? 119.9 : 109.9, From 67cd0d0ab21e5f2eb9609f72503d4f35afb5d00a Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Wed, 3 Jan 2024 06:32:24 +0700 Subject: [PATCH 20/33] fix: update x position for text and icon if locale is right to left (#222) --- src/card.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/card.ts b/src/card.ts index efd1fca..37ff40f 100644 --- a/src/card.ts +++ b/src/card.ts @@ -27,9 +27,9 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { const angle = { titleXAngle: isDisabledAnimations ? (isRtlDirection ? 520 : 15) : (isRtlDirection ? 510 : 5), titleYAngle: isDisabledAnimations ? 0 : -10, - textXAngle: isRtlDirection ? 205 : 20, + textXAngle: isRtlDirection ? 215 : 20, dataXAngle: isRtlDirection ? 15 : 220, - iconXAngle: isRtlDirection ? 215 : -5, + iconXAngle: isRtlDirection ? 225 : -5, imageXAngle: isDisabledAnimations ? 120 : 125, imageYAngle: isDisabledAnimations ? 70 : 65, userXAngle: isDisabledAnimations ? 119.9 : 109.9, From 6bc50402af4c8cab95476e9aa5fb2f5b26e3332f Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Wed, 3 Jan 2024 06:38:40 +0700 Subject: [PATCH 21/33] chore: update .npmignore --- .npmignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.npmignore b/.npmignore index a5cfb4a..0a09d5c 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,10 @@ .env.example +coverage.yml +jest.config.json + .github/ tests/ scripts/ +node_modules/ coverage/ public/ From 936e29fc537ef1b042a3f98d19fb01ec26236903 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:24:20 +0700 Subject: [PATCH 22/33] docs(readme): Update copyright year --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 30f5de7..5b7af55 100644 --- a/README.md +++ b/README.md @@ -368,7 +368,7 @@ Want to contribute? Please note our contribution guidelines [here](/CONTRIBUTING

Made with ❤️ and TypeScript

-

Copyright © 2023 Rangga Fajar Oktariansyah

+

Copyright © 2023-present Rangga Fajar Oktariansyah

GitHub Readme Profile License
From fc7489ce746333229e88a251262681778991b644 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Fri, 5 Jan 2024 09:45:17 +0700 Subject: [PATCH 23/33] feat: include all commits from the year the user joined (#223) --- src/card.ts | 4 +- src/fetcher/apiFetch.ts | 118 +++++++++++++++++++++++++++++++++++++--- src/getData.ts | 6 +- 3 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/card.ts b/src/card.ts index 37ff40f..31de846 100644 --- a/src/card.ts +++ b/src/card.ts @@ -143,7 +143,7 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { ${gradientStops} - `; + `; } // Generating SVG code for the background based on background color or gradient @@ -158,7 +158,7 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { backgroundSVG = generateGradient(gradientColors); } else { backgroundSVG = ` - `; + `; } } } diff --git a/src/fetcher/apiFetch.ts b/src/fetcher/apiFetch.ts index 475b1f5..f3dc2d1 100644 --- a/src/fetcher/apiFetch.ts +++ b/src/fetcher/apiFetch.ts @@ -10,7 +10,6 @@ export interface User { repositories: Repositories; followers: Followers; following: Following; - contributionsCollection: ContributionsCollection; openedIssues: OpenedIssues; closedIssues: ClosedIssues; pullRequests: PullRequests; @@ -18,6 +17,9 @@ export interface User { discussionStarted: DiscussionStarted; discussionAnswered: DiscussionAnswered; repositoriesContributedTo: RepositoriesContributedTo; + totalCommitContributions: number; + restrictedContributionsCount: number; + totalPullRequestReviewContributions: number; } // Represents the total count of repositories @@ -77,6 +79,80 @@ export interface RepositoriesContributedTo { totalCount: number; } +async function getUserJoinYear(username: string): Promise { + const data = await axios({ + method: "post", + url: "https://api.github.com/graphql", + headers: { + "User-Agent": "FajarKim/github-readme-profile", + Authorization: getRandomToken(true), + }, + data: { + query: `query userInfo($username: String!) { + user(login: $username) { + createdAt + } + }`, + variables: { + username, + }, + }, + }); + + if (data.data.errors?.length > 0) { + throw new Error(data.data.errors[0].message); + } + + const user = data.data.data.user; + if (!user || !user.createdAt) { + throw new Error("User data is missing."); + } + + const joinDate = new Date(user.createdAt); + return joinDate.getFullYear(); +} + +async function fetchContributions(username: string, year: number): Promise { + const from = `${year}-01-01T00:00:00Z`; + const to = `${year}-12-31T23:59:59Z`; + + const data = await axios({ + method: "post", + url: "https://api.github.com/graphql", + headers: { + "User-Agent": "FajarKim/github-readme-profile", + Authorization: getRandomToken(true), + }, + data: { + query: `query userInfo($username: String!, $from: DateTime!, $to: DateTime!) { + user(login: $username) { + contributionsCollection(from: $from, to: $to) { + totalCommitContributions + restrictedContributionsCount + totalPullRequestReviewContributions + } + } + }`, + variables: { + username, + from, + to, + }, + }, + }); + + if (data.data.errors?.length > 0) { + throw new Error(data.data.errors[0].message); + } + + const contributions = data.data.data.user.contributionsCollection; + return { + totalCommitContributions: contributions.totalCommitContributions, + restrictedContributionsCount: contributions.restrictedContributionsCount, + totalPullRequestReviewContributions: contributions.totalPullRequestReviewContributions, + }; +} + /** * Fetches user data from the GitHub GraphQL API. * @@ -84,6 +160,20 @@ export interface RepositoriesContributedTo { * @returns {Promise} Promise representing the user data obtained from the GitHub Graphql API. */ export default async function apiFetch(username: string): Promise { + const startYear = await getUserJoinYear(username); + const endYear = new Date().getFullYear(); + + let TotalCommitContributions = 0; + let RestrictedContributionsCount = 0; + let TotalPullRequestReviewContributions = 0; + + for (let year = startYear; year <= endYear; year++) { + const contributions = await fetchContributions(username, year); + TotalCommitContributions += contributions.totalCommitContributions; + RestrictedContributionsCount += contributions.restrictedContributionsCount; + TotalPullRequestReviewContributions += contributions.totalPullRequestReviewContributions; + } + const data = await axios({ method: "post", url: "https://api.github.com/graphql", @@ -106,11 +196,6 @@ export default async function apiFetch(username: string): Promise { following { totalCount } - contributionsCollection { - totalCommitContributions - restrictedContributionsCount - totalPullRequestReviewContributions - } openedIssues: issues(states: OPEN) { totalCount } @@ -143,5 +228,24 @@ export default async function apiFetch(username: string): Promise { if (data.data.errors?.length > 0) throw new Error(data.data.errors[0].message); - return data.data.data.user; + const user = data.data.data.user; + + return { + name: user.name, + login: user.login, + avatarUrl: user.avatarUrl, + repositories: user.repositories, + followers: user.followers, + following: user.following, + openedIssues: user.openedIssues, + closedIssues: user.closedIssues, + pullRequests: user.pullRequests, + mergedPullRequests: user.mergedPullRequests, + discussionStarted: user.discussionStarted, + discussionAnswered: user.discussionAnswered, + repositoriesContributedTo: user.repositoriesContributedTo, + totalCommitContributions: TotalCommitContributions, + restrictedContributionsCount: RestrictedContributionsCount, + totalPullRequestReviewContributions: TotalPullRequestReviewContributions, + }; } diff --git a/src/getData.ts b/src/getData.ts index cd344d2..d563189 100644 --- a/src/getData.ts +++ b/src/getData.ts @@ -58,10 +58,10 @@ async function getData(username: string): Promise { total_prs: millify(user.pullRequests.totalCount), total_prs_merged: millify(user.mergedPullRequests.totalCount), total_commits: millify( - user.contributionsCollection.restrictedContributionsCount + - user.contributionsCollection.totalCommitContributions + user.restrictedContributionsCount + + user.totalCommitContributions ), - total_review: millify(user.contributionsCollection.totalPullRequestReviewContributions), + total_review: millify(user.totalPullRequestReviewContributions), total_discussion_answered: millify(user.discussionAnswered.totalCount), total_discussion_started: millify(user.discussionStarted.totalCount), total_contributed_to: millify(user.repositoriesContributedTo.totalCount), From 04db312af7e992a20a1adc25fb2bbc1c5a3b2d90 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:03:10 +0700 Subject: [PATCH 24/33] refactor: revise code for readability and maintainability (#224) --- api/index.ts | 58 +++--- scripts/generate-theme-doc.ts | 30 +-- scripts/generate-translation-doc.ts | 21 +-- scripts/languageNames.ts | 23 ++- src/card.ts | 34 +--- src/common/utils.ts | 20 +- src/fetcher/repositoryFetch.ts | 87 --------- src/fetcher/repositoryStats.ts | 97 ++++++++++ src/fetcher/{apiFetch.ts => stats.ts} | 174 ++++++++++++++---- src/getData.ts | 50 +++-- src/getRandomToken.ts | 39 ---- src/getToken.ts | 39 ++++ src/icons.ts | 33 +++- src/translations.ts | 35 +++- tests/getToken.test.ts | 58 ++++++ tests/randomToken.test.ts | 45 ----- ...tsCard.test.ts => renderStatsCard.test.ts} | 63 +++++-- themes/index.ts | 28 ++- withexpress.ts | 2 +- 19 files changed, 574 insertions(+), 362 deletions(-) delete mode 100644 src/fetcher/repositoryFetch.ts create mode 100644 src/fetcher/repositoryStats.ts rename src/fetcher/{apiFetch.ts => stats.ts} (51%) delete mode 100644 src/getRandomToken.ts create mode 100644 src/getToken.ts create mode 100644 tests/getToken.test.ts delete mode 100644 tests/randomToken.test.ts rename tests/{renderAPIStatsCard.test.ts => renderStatsCard.test.ts} (65%) diff --git a/api/index.ts b/api/index.ts index 4ff2868..803b1ae 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1,13 +1,32 @@ -// Importing necessary libraries and modules import escapeHTML from "escape-html"; import { Resvg } from "@resvg/resvg-js"; import getData from "../src/getData"; -import cardStyle from "../src/card"; +import card from "../src/card"; import { themes, Themes } from "../themes/index"; import { isValidHexColor, isValidGradient, parseBoolean } from "../src/common/utils"; -// User interface configuration type -export type UiConfig = { +/** + * Type representing the configuration for the card options. + * + * @typedef {Object} UiConfig + * @property {string} titleColor - Color for the title text. + * @property {string} textColor - Color for the main text. + * @property {string} iconColor - Color for icons. + * @property {string} borderColor - Color for borders. + * @property {string} strokeColor - Color for strokes. + * @property {string} usernameColor - Color for the username. + * @property {any} bgColor - Background color or gradient. + * @property {string} Locale - Locale setting. + * @property {number|string} borderWidth - Width of borders. + * @property {number|string} borderRadius - Radius of borders. + * @property {boolean|string} disabledAnimations - Toggle for disabling animations. + * @property {string} Format - Output format (e.g., "svg", "png", or "json"). + * @property {string|undefined} hiddenItems - Items to hide. + * @property {string|undefined} showItems - Items to show. + * @property {boolean|string} hideStroke - Toggle for hiding strokes. + * @property {boolean|string} hideBorder - Toggle for hiding borders. + */ +type UiConfig = { titleColor: string; textColor: string; iconColor: string; @@ -27,23 +46,20 @@ export type UiConfig = { }; /** - * Generates readme stats based on user input. + * Handles the generation card of a GitHub stats based on user data and specified options. * - * @param {any} req HTTP request object. - * @param {any} res HTTP response object. - * @returns {Promise} Promise representing the response. + * @param {any} req - The request object from the client. + * @param {any} res - The response object to send data back to the client. + * @returns {Promise} - A promise that resolves when the photo profile is generated and sent. */ -export default async function readmeStats(req: any, res: any): Promise { +async function readmeStats(req: any, res: any): Promise { try { - // Extracting and escaping username from the request const username = escapeHTML(req.query.username); - // Setting fallback and default themes const fallbackTheme = "default"; const defaultTheme: Themes[keyof Themes] = themes[fallbackTheme]; const selectTheme: Themes[keyof Themes] = themes[req.query.theme] || defaultTheme; - // Configuring UI based on request parameters or using default values const uiConfig: UiConfig = { titleColor: escapeHTML(req.query.title_color || selectTheme.title_color || defaultTheme.title_color), textColor: escapeHTML(req.query.text_color || selectTheme.text_color || defaultTheme.text_color), @@ -63,7 +79,6 @@ export default async function readmeStats(req: any, res: any): Promise { hideBorder: parseBoolean(escapeHTML(req.query.hide_border)) || false, }; - // Validating username and color codes if (!username) { throw new Error("Username is required"); } @@ -79,38 +94,35 @@ export default async function readmeStats(req: any, res: any): Promise { throw new Error("Enter a valid hex color code"); } - // Validating background color or gradient if (!isValidGradient(uiConfig.bgColor)) { if (!isValidHexColor(uiConfig.bgColor)) { throw new Error("Enter a valid hex color code"); } } - // Fetching user stats data const fetchStats = await getData(username); res.setHeader("Cache-Control", "s-maxage=3600, stale-while-revalidate"); - // Handling different response formats (JSON, PNG, SVG) if (uiConfig.Format === "json") { res.json(fetchStats); } else if (uiConfig.Format === "png") { - // Converting SVG to PNG using @resvg/resvg-js - const svgString = cardStyle(fetchStats, uiConfig); + const svgString = card(fetchStats, uiConfig); const resvg = new Resvg(svgString, { font: { defaultFontFamily: "Segoe UI" }}); const pngBuffer = await resvg.render().asPng(); - // Sending PNG in the response res.setHeader("Content-Type", "image/png"); res.send(pngBuffer); } else { - // Sending SVG in the response res.setHeader("Content-Type", "image/svg+xml"); - const svg = cardStyle(fetchStats, uiConfig); + const svg = card(fetchStats, uiConfig); res.send(svg); } } catch (error: any) { - // Handling and logging errors in the response + const message = error.message; res.setHeader("Cache-Control", "s-maxage=7200, stale-while-revalidate"); - res.status(500).send(escapeHTML(error.message)); + res.status(500).send(escapeHTML(message)); } } + +export { UiConfig, readmeStats }; +export default readmeStats; diff --git a/scripts/generate-theme-doc.ts b/scripts/generate-theme-doc.ts index e8ab605..f3f04a8 100644 --- a/scripts/generate-theme-doc.ts +++ b/scripts/generate-theme-doc.ts @@ -1,43 +1,20 @@ -// Importing necessary libraries and modules import fs from "fs"; -import { themes } from "../themes/index"; +import themes from "../themes/index"; const TARGET_FILE = "./themes/README.md"; -/** - * Generates the markdown for a theme. - * - * @param {string} theme Theme name. - * @returns {string} Theme markdown string. - */ function generateThemeMarkdown(theme: string): string { - // Creates a markdown string for a given theme return `\`${theme}\` ![${theme}][${theme}]`; } -/** - * Generates the link to the theme image for a username and theme. - * - * @param {string} username GitHub username. - * @param {string} theme Theme name. - * @returns {string} Theme link string. - */ function generateThemeLink(username: string, theme: string): string { - // Creates a link to the theme image for a specific username and theme return `[${theme}]: https://github-readme-profile-alpha.vercel.app/api?username=${username}&theme=${theme}`; } -/** - * Generates the content for the README file with available themes and preview links. - * - * @param {string} username GitHub username. - * @returns {string} README content string. - */ export function generateReadmeThemes(username: string): string { const availableThemes = Object.keys(themes); const itemsPerRow = 3; - // Generate table rows with theme markdown let themesPreviewTable = ""; for (let i = 0; i < availableThemes.length; i += itemsPerRow) { const themesSlice = availableThemes.slice(i, i + itemsPerRow); @@ -45,7 +22,6 @@ export function generateReadmeThemes(username: string): string { themesPreviewTable += `| ${row} |\n`; } - // Generate individual theme links let themesPreviewLink = ""; for (let i = 0; i < availableThemes.length; i += 1) { const themesSlice = availableThemes.slice(i, i + 1); @@ -53,7 +29,6 @@ export function generateReadmeThemes(username: string): string { themesPreviewLink += `${row}\n`; } - // Final README content with themes and links const readmeContent = ` ## Available Themes @@ -78,11 +53,8 @@ ${themesPreviewLink}`; return readmeContent; } -// GitHub username to generate the README const username = "FajarKim"; -// Generate the README content const generatedReadme = generateReadmeThemes(username); -// Write the README content to the specified file fs.writeFileSync(TARGET_FILE, generatedReadme); diff --git a/scripts/generate-translation-doc.ts b/scripts/generate-translation-doc.ts index 6dfc5fc..fdce7e5 100644 --- a/scripts/generate-translation-doc.ts +++ b/scripts/generate-translation-doc.ts @@ -1,30 +1,16 @@ -// Importing necessary libraries and modules import fs from "fs"; -import { locales } from "../src/translations"; -import { languageNames } from "./languageNames"; +import locales from "../src/translations"; +import languageNames from "./languageNames"; const TARGET_FILE = "./docs/translations.md"; -/** - * Generates the markdown for a locale. - * - * @param {string} locale Locale code. - * @returns {string} Locale markdown string. - */ function generateTranslationsMarkdown(locale: string): string { - // Returns the markdown string for a specific locale return `${locale}`; } -/** - * Generates the content for the README file with available locales and progress. - * - * @returns {string} README content string. - */ export function generateReadmeLocales(): string { const availableLocales = Object.keys(locales); - // Generate table rows with locale details let localesListTable = ""; for (let i = 0; i < availableLocales.length; i += 1) { const localesSlice = availableLocales.slice(i, i + 1); @@ -36,7 +22,6 @@ export function generateReadmeLocales(): string { \n`; } - // Final README content with locales and progress const readmeContent = ` ## Available Locales Use \`?locale=LOCALE_CODE\` parameter like so :- @@ -61,8 +46,6 @@ Want to add new translations? Consider reading the [contribution guidelines](htt return readmeContent; } -// Generate the README content const generatedReadme = generateReadmeLocales(); -// Write the README content to the specified file fs.writeFileSync(TARGET_FILE, generatedReadme); diff --git a/scripts/languageNames.ts b/scripts/languageNames.ts index 411a5d4..b2d7565 100644 --- a/scripts/languageNames.ts +++ b/scripts/languageNames.ts @@ -1,10 +1,20 @@ -// Represents the structure of localized code languages name in English -export type LanguageNames = { +/** + * Type representing a collection of language names based on language codes. + * + * @typedef {Object} LanguageNames + * @property {string} [key] - Language code. + * @property {string} [value] - Language name. + */ +type LanguageNames = { [key: string]: string; }; -// Object containing localized strings for different languages name in English -export const languageNames: LanguageNames = { +/** + * LanguageNames object stores language code and language name. + * + * @type {LanguageNames} + */ +const languageNames: LanguageNames = { "af": "Afrikaans", "af-NA": "Afrikaans (Namibia)", "af-ZA": "Afrikaans (South Africa)", @@ -810,4 +820,7 @@ export const languageNames: LanguageNames = { "zh-Hant-TW": "Chinese (Traditional, Taiwan)", "zu": "Zulu", "zu-ZA": "Zulu (South Africa)", -} +}; + +export { LanguageNames, languageNames }; +export default languageNames; diff --git a/src/card.ts b/src/card.ts index 31de846..4dc2615 100644 --- a/src/card.ts +++ b/src/card.ts @@ -1,28 +1,24 @@ -// Importing necessary types and modules import type { GetData } from "./getData"; import type { UiConfig } from "../api/index"; import { locales, Locales } from "./translations"; -import { icons } from "./icons"; +import icons from "./icons"; import { parseBoolean } from "./common/utils"; /** - * Generates the SVG code for the user's GitHub stats card based on provided data and UI configuration. + * Generates the SVG markup for the GitHub stats card. * - * @param {GetData} data User's GitHub stats data. - * @param {UiConfig} uiConfig User interface configuration. - * @returns {string} SVG code representing the GitHub stats card. + * @param {GetData} data - GitHub user data stats. + * @param {UiConfig} uiConfig - Configuration for the UI card options. + * @returns {string} - SVG markup for the GitHub stats card. */ -export default function cardStyle(data: GetData, uiConfig: UiConfig): string { - // Setting fallback and default locale +function card(data: GetData, uiConfig: UiConfig): string { const fallbackLocale = "en"; const defaultLocale: Locales[keyof Locales] = locales[fallbackLocale]; const selectLocale: Locales[keyof Locales] = locales[uiConfig.Locale] || defaultLocale; - // Determining text direction and animations based on UI configuration const isRtlDirection = parseBoolean(selectLocale.rtlDirection); const isDisabledAnimations = parseBoolean(uiConfig.disabledAnimations || uiConfig.Format === "png"); - // Setting angles and positions for SVG elements const direction = isRtlDirection ? "rtl" : "ltr"; const angle = { titleXAngle: isDisabledAnimations ? (isRtlDirection ? 520 : 15) : (isRtlDirection ? 510 : 5), @@ -38,11 +34,9 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { follYAngle: isDisabledAnimations ? 161 : 151, }; - // Setting styles for hiding stroke and border based on UI configuration const hideStroke = parseBoolean(uiConfig.hideStroke) ? `` : `stroke="#${uiConfig.strokeColor}" stroke-width="5"`; const hideBorder = parseBoolean(uiConfig.hideBorder) ? `` : `stroke="#${uiConfig.borderColor}" stroke-opacity="1" stroke-width="${uiConfig.borderWidth}"`; - // CSS animations for SVG elements const animations = parseBoolean(uiConfig.disabledAnimations || uiConfig.Format === "png") ? `` : ` /* Animations */ @keyframes scaleInAnimation { @@ -86,13 +80,11 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { animation: fadeInAnimation 0.3s ease-in-out forwards; }`; - // Extracting and formatting hidden and shown items based on UI configuration const hiddenItems = uiConfig.hiddenItems || ""; const hiddenItemsArray = hiddenItems.split(","); const showItems = uiConfig.showItems || ""; const showItemsArray = showItems.split(","); - // Array containing information about different GitHub stats to be displayed on the card const cardItems = [ { text: selectLocale.totalReposText || defaultLocale.totalReposText, value: data.public_repos, icon: icons.repository, hidden: hiddenItemsArray.includes("repos") }, { text: selectLocale.starsCountText || defaultLocale.starsCountText, value: data.total_stars, icon: icons.star, hidden: hiddenItemsArray.includes("stars") }, @@ -108,10 +100,8 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { { text: selectLocale.contributedToText || defaultLocale.contributedToText, value: data.total_contributed_to, icon: icons.contributed_to, hidden: hiddenItemsArray.includes("contributed") }, ]; - // Filtering items based on hiddenItemsArray and showItemsArray const cardItemsToShow = cardItems.filter(item => !item.hidden); - // Generating SVG code for each visible card item const cardItemsSVG = cardItemsToShow.map((item, index) => ` @@ -123,12 +113,6 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { `).join("\n"); - /** - * Generates a linear gradient SVG code based on an array of color stops. - * - * @param {string[]} colors Array of color codes for the gradient stops. - * @returns {string} SVG code representing the linear gradient. - */ function generateGradient(colors: string[]): string { const gradientId = "gradient"; const gradientAngle = colors[0]; @@ -146,7 +130,6 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { `; } - // Generating SVG code for the background based on background color or gradient let backgroundSVG; if (uiConfig.bgColor) { if (Array.isArray(uiConfig.bgColor)) { @@ -163,7 +146,6 @@ export default function cardStyle(data: GetData, uiConfig: UiConfig): string { } } - // Final SVG code for the GitHub stats card return ` `; } + +export default card; diff --git a/src/common/utils.ts b/src/common/utils.ts index 3b8ffa5..48ae38a 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -1,8 +1,8 @@ /** - * Checks if the provided value is a valid hexadecimal color code. + * Checks whether the provided value is a valid hexadecimal color. * - * @param {any} hexColor Value to be checked. - * @returns {boolean} True if the value is a valid hexadecimal color code, false otherwise. + * @param {any} hexColor - The input value to check for valid hexadecimal color. + * @returns {boolean} - True if the input is a valid hexadecimal color, false otherwise. */ function isValidHexColor(hexColor: any): boolean { return new RegExp( @@ -11,10 +11,10 @@ function isValidHexColor(hexColor: any): boolean { } /** - * Checks if the provided array of color codes represents a valid gradient. + * Checks whether the provided array of hexadecimal colors is a valid gradient. * - * @param {string[]} hexColors Array of color codes to be checked. - * @returns {boolean} True if the array represents a valid gradient, false otherwise. + * @param {string[]} hexColors - The array of hexadecimal colors to check for a valid gradient. + * @returns {boolean} - True if the array represents a valid gradient, false otherwise. */ function isValidGradient(hexColors: string[]): boolean { const colors = [hexColors]; @@ -27,10 +27,10 @@ function isValidGradient(hexColors: string[]): boolean { } /** - * Parses a boolean value or string representation of a boolean. + * Parses a boolean value from either a boolean or string representation. * - * @param {boolean | string} value Value to be parsed. - * @returns {boolean | undefined} Parsed boolean value or undefined if parsing fails. + * @param {boolean | string} value - The input value to parse as a boolean. + * @returns {boolean | undefined} - The parsed boolean value, or undefined if parsing fails. */ function parseBoolean(value: boolean | string): boolean | undefined { if (typeof value === "boolean") { @@ -51,4 +51,4 @@ export { isValidHexColor, isValidGradient, parseBoolean -} \ No newline at end of file +} diff --git a/src/fetcher/repositoryFetch.ts b/src/fetcher/repositoryFetch.ts deleted file mode 100644 index 1eb4532..0000000 --- a/src/fetcher/repositoryFetch.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Importing necessary libraries and modules -import axios from "axios"; -import getRandomToken from "../getRandomToken"; - -// Represents the structure returned: Promise containing the repository data -type RepositoryData = { - stars: number; - forks: number; - openedIssues: number; -}; - -/** - * Fetches repository data for the specified user. - * - * @param {string} username GitHub username. - * @param {number} totalpage Total pages of repositories to fetch. - * @returns {Promise} Promise containing the repository data. - */ -export default async function repositoryFetch( - username: string, - totalpage: number -): Promise { - let stars = 0; - let forks = 0; - let openedIssues = 0; - - // Fetch repository data for each page in parallel - await Promise.all( - Array.from( - { length: totalpage }, - async (_, i) => await getPerPageReposData(username, i + 1) - ) - ).then((data: object[]) => { - // Accumulate data from each page - data.forEach((repo: any) => { - stars += repo.stars; - forks += repo.forks; - openedIssues += repo.openedIssues; - }); - }); - - return { - stars, - forks, - openedIssues, - }; -} - -/** - * Fetches repository data for a specific page. - * - * @param {string} username GitHub username. - * @param {number} pageno Page number to fetch. - * @returns {Promise} Promise containing the repository data for the specified page. - */ -async function getPerPageReposData( - username: string, - pageno: number -): Promise { - const sanitizedUsername = encodeURIComponent(username); - // Fetch data for the specified page - const data = await axios({ - method: "get", - url: `https://api.github.com/users/${sanitizedUsername}/repos?page=${pageno}&per_page=100`, - headers: { - "User-Agent": "FajarKim/github-readme-profile", - Authorization: getRandomToken(true), - }, - }); - - let stars = 0; - let forks = 0; - let openedIssues = 0; - - // Accumulate data from each repository on the page - data.data.forEach((repo: any) => { - stars += repo.stargazers_count; - forks += repo.forks_count; - openedIssues += repo.open_issues; - }); - - return { - stars, - forks, - openedIssues, - }; -} diff --git a/src/fetcher/repositoryStats.ts b/src/fetcher/repositoryStats.ts new file mode 100644 index 0000000..119afe8 --- /dev/null +++ b/src/fetcher/repositoryStats.ts @@ -0,0 +1,97 @@ +import axios from "axios"; +import getToken from "../getToken"; + +/** + * Type representing the data associated with a user's repository stats. + * + * @typedef {Object} RepositoryData + * @property {number} stars - The total count of stars across repositories. + * @property {number} forks - The total count of forks across repositories. + * @property {number} openedIssues - The total count of opened issues across repositories. + */ +type RepositoryData = { + stars: number; + forks: number; + openedIssues: number; +}; + +/** + * Retrieves and calculates repository statistics for a given user. + * + * @param {string} username - The username of the GitHub user. + * @param {number} totalpage - The total number of pages to retrieve data from. + * @returns {Promise} - A promise that resolves to the repository statistics. + */ +async function repositoryStats( + username: string, + totalpage: number +): Promise { + let stars = 0; + let forks = 0; + let openedIssues = 0; + + await Promise.all( + Array.from( + { length: totalpage }, + async (_, i) => await getPerPageRepositoryData(username, i + 1) + ) + ).then((data: object[]) => { + data.forEach((repo: any) => { + stars += repo.stars; + forks += repo.forks; + openedIssues += repo.openedIssues; + }); + }); + + return { + stars, + forks, + openedIssues, + }; +} + +/** + * Retrieves repository data for a specific page. + * + * @param {string} username - The username of the GitHub user. + * @param {number} pageno - The page number to retrieve data from. + * @returns {Promise} - A promise that resolves to the repository data for the specified page. + */ +async function getPerPageRepositoryData( + username: string, + pageno: number +): Promise { + const sanitizedUsername = encodeURIComponent(username); + + const data = await axios({ + method: "get", + url: `https://api.github.com/users/${sanitizedUsername}/repos?page=${pageno}&per_page=100`, + headers: { + "User-Agent": "FajarKim/github-readme-profile", + Authorization: getToken(true), + }, + }); + + let stars = 0; + let forks = 0; + let openedIssues = 0; + + data.data.forEach((repo: any) => { + stars += repo.stargazers_count; + forks += repo.forks_count; + openedIssues += repo.open_issues; + }); + + return { + stars, + forks, + openedIssues, + }; +} + +export { + RepositoryData, + repositoryStats, + getPerPageRepositoryData +}; +export default repositoryStats; diff --git a/src/fetcher/apiFetch.ts b/src/fetcher/stats.ts similarity index 51% rename from src/fetcher/apiFetch.ts rename to src/fetcher/stats.ts index f3dc2d1..4d4ae29 100644 --- a/src/fetcher/apiFetch.ts +++ b/src/fetcher/stats.ts @@ -1,9 +1,28 @@ -// Importing necessary libraries and modules import axios from "axios"; -import getRandomToken from "../getRandomToken"; +import getToken from "../getToken"; -// Represents the user data obtained from the GitHub GraphQL API -export interface User { +/** + * Represents a user's information and statistics. + * + * @interface User + * @property {string} name - The name of the user. + * @property {string} login - The login username of the user. + * @property {string} avatarUrl - The URL of the user's avatar. + * @property {Repositories} repositories - Information about user repositories. + * @property {Followers} followers - Information about user followers. + * @property {Following} following - Information about users being followed by the user. + * @property {OpenedIssues} openedIssues - Information about opened issues by the user. + * @property {ClosedIssues} closedIssues - Information about closed issues by the user. + * @property {PullRequests} pullRequests - Information about user pull requests. + * @property {MergedPullRequests} mergedPullRequests - Information about merged pull requests by the user. + * @property {DiscussionStarted} discussionStarted - Information about discussions started by the user. + * @property {DiscussionAnswered} discussionAnswered - Information about discussions answered by the user. + * @property {RepositoriesContributedTo} repositoriesContributedTo - Information about repositories contributed to by the user. + * @property {number} totalCommitContributions - The total count of commit contributions by the user. + * @property {number} restrictedContributionsCount - The count of restricted contributions by the user. + * @property {number} totalPullRequestReviewContributions - The total count of pull request review contributions by the user. + */ +interface User { name: string; login: string; avatarUrl: string; @@ -22,70 +41,133 @@ export interface User { totalPullRequestReviewContributions: number; } -// Represents the total count of repositories -export interface Repositories { +/** + * Represents information about user repositories. + * + * @interface Repositories + * @property {number} totalCount - The total count of repositories. + */ +interface Repositories { totalCount: number; } -// Represents the total count of followers -export interface Followers { +/** + * Represents information about user followers. + * + * @interface Followers + * @property {number} totalCount - The total count of followers. + */ +interface Followers { totalCount: number; } -// Represents the total count of following -export interface Following { +/** + * Represents information about user following. + * + * @interface Following + * @property {number} totalCount - The total count of following. + */ +interface Following { totalCount: number; } -// Represents contribution data -export interface ContributionsCollection { +/** + * Represents contributions data collected over a specific time period. + * + * @interface ContributionsCollection + * @property {number} totalCommitContributions - The total count of commit contributions. + * @property {number} restrictedContributionsCount - The count of restricted contributions. + * @property {number} totalPullRequestReviewContributions - The total count of pull request review contributions. + */ +interface ContributionsCollection { totalCommitContributions: number; restrictedContributionsCount: number; totalPullRequestReviewContributions: number; } -// Represents the total count of opened issues -export interface OpenedIssues { +/** + * Represents information about user opened issues. + * + * @interface OpenedIssues + * @property {number} totalCount - The total count of opened issues. + */ +interface OpenedIssues { totalCount: number; } -// Represents the total count of closed issues -export interface ClosedIssues { +/** + * Represents information about user closed issues. + * + * @interface ClosedIssues + * @property {number} totalCount - The total count of closed issues. + */ +interface ClosedIssues { totalCount: number; } -// Represents the total count of pull requests -export interface PullRequests { +/** + * Represents information about user pull requests. + * + * @interface PullRequests + * @property {number} totalCount - The total count of pull requests. + */ +interface PullRequests { totalCount: number; } -// Represents the total count of merged pull requests -export interface MergedPullRequests { +/** + * Represents information about user merged pull requests. + * + * @interface MergedPullRequests + * @property {number} totalCount - The total count of merged pull requests. + */ +interface MergedPullRequests { totalCount: number; } -// Represents the total count of started discussions -export interface DiscussionStarted { +/** + * Represents information about user discussion started. + * + * @interface DiscussionStarted + * @property {number} totalCount - The total count of discussion started. + */ +interface DiscussionStarted { totalCount: number; } -// Represents the total count of answered discussions -export interface DiscussionAnswered { +/** + * Represents information about user discussion answered. + * + * @interface DiscussionAnswered + * @property {number} totalCount - The total count of discussion answered. + */ +interface DiscussionAnswered { totalCount: number; } -// Represents the total count of repositories contributed to -export interface RepositoriesContributedTo { +/** + * Represents information about user repositories contributed to. + * + * @interface RepositoriesContributedTo + * @property {number} totalCount - The total count of repositories contributed to. + */ +interface RepositoriesContributedTo { totalCount: number; } +/** + * Fetches the user's join year based on the provided username. + * + * @param {string} username - The GitHub username of the user. + * @returns {Promise} - A promise that resolves with the user's join year. + */ async function getUserJoinYear(username: string): Promise { const data = await axios({ method: "post", url: "https://api.github.com/graphql", headers: { "User-Agent": "FajarKim/github-readme-profile", - Authorization: getRandomToken(true), + Authorization: getToken(true), }, data: { query: `query userInfo($username: String!) { @@ -112,6 +194,13 @@ async function getUserJoinYear(username: string): Promise { return joinDate.getFullYear(); } +/** + * Fetches the contributions data for the specified year. + * + * @param {string} username - The GitHub username of the user. + * @param {number} year - The year for which contributions data is fetched. + * @returns {Promise} - A promise that resolves with contributions data. + */ async function fetchContributions(username: string, year: number): Promise { const from = `${year}-01-01T00:00:00Z`; const to = `${year}-12-31T23:59:59Z`; @@ -121,7 +210,7 @@ async function fetchContributions(username: string, year: number): Promise} Promise representing the user data obtained from the GitHub Graphql API. + * @param {string} username - The GitHub username of the user. + * @returns {Promise} - A promise that resolves with the user's information and statistics. */ -export default async function apiFetch(username: string): Promise { +async function stats(username: string): Promise { const startYear = await getUserJoinYear(username); const endYear = new Date().getFullYear(); @@ -179,7 +268,7 @@ export default async function apiFetch(username: string): Promise { url: "https://api.github.com/graphql", headers: { "User-Agent": "FajarKim/github-readme-profile", - Authorization: getRandomToken(true), + Authorization: getToken(true), }, data: { query: `query userInfo($username: String!) { @@ -249,3 +338,22 @@ export default async function apiFetch(username: string): Promise { totalPullRequestReviewContributions: TotalPullRequestReviewContributions, }; } + +export { + User, + Repositories, + Followers, + Following, + ContributionsCollection, + OpenedIssues, + ClosedIssues, + PullRequests, + MergedPullRequests, + DiscussionStarted, + DiscussionAnswered, + RepositoriesContributedTo, + getUserJoinYear, + fetchContributions, + stats +}; +export default stats; diff --git a/src/getData.ts b/src/getData.ts index d563189..23e61d3 100644 --- a/src/getData.ts +++ b/src/getData.ts @@ -1,14 +1,34 @@ -// Importing necessary libraries and modules import millify from "millify"; -import apiFetch from "./fetcher/apiFetch"; -import repositoryFetch from "./fetcher/repositoryFetch"; +import stats from "./fetcher/stats"; +import repositoryStats from "./fetcher/repositoryStats"; const base64ImageFetcher = require("node-base64-image"); -// Represents the data structure returned by the getData function -export type GetData = { +/** + * Type representing GitHub user information. + * + * @typedef {Object} GetData + * @property {string} username - GitHub username. + * @property {string} name - GitHub user's name. + * @property {string|Buffer} picture - Base64-encoded image or Buffer representing the user's profile picture. + * @property {string|number} public_repos - Formatted count of public repositories. + * @property {string|number} followers - Formatted count of followers. + * @property {string|number} following - Formatted count of users being followed. + * @property {string|number} total_stars - Formatted count of total stars received on repositories. + * @property {string|number} total_forks - Formatted count of total forks received on repositories. + * @property {string|number} total_issues - Formatted count of total issues (both open and closed). + * @property {string|number} total_closed_issues - Formatted count of closed issues. + * @property {string|number} total_prs - Formatted count of total pull requests. + * @property {string|number} total_prs_merged - Formatted count of total merged pull requests. + * @property {string|number} total_commits - Formatted count of total commits. + * @property {string|number} total_review - Formatted count of pull request reviews. + * @property {string|number} total_discussion_answered - Formatted count of discussions answered. + * @property {string|number} total_discussion_started - Formatted count of discussions started. + * @property {string|number} total_contributed_to - Formatted count of repositories contributed to. + */ +type GetData = { username: string; name: string; - pic: string | Buffer; + picture: string | Buffer; public_repos: string | number; followers: string | number; following: string | number; @@ -26,24 +46,22 @@ export type GetData = { }; /** - * Fetches and processes data for a given GitHub user. + * Fetches and formats GitHub user data based on the provided username. * - * @param {string} username GitHub username. - * @returns Promise Promise representing the data structure returned. + * @param {string} username - GitHub username. + * @returns {Promise} - A promise that resolves with formatted GitHub user data. */ async function getData(username: string): Promise { - const user = await apiFetch(username); + const user = await stats(username); const totalRepoPages = Math.ceil(user.repositories.totalCount / 100); - const userRepositories = await repositoryFetch(username, totalRepoPages); + const userRepositories = await repositoryStats(username, totalRepoPages); - // If user.name is not available, use user.login as name if (!user.name) user.name = user.login; - // Creating output object const output = { username: user.login, name: user.name, - pic: await base64ImageFetcher.encode(`${user.avatarUrl}&s=200`, { + picture: await base64ImageFetcher.encode(`${user.avatarUrl}&s=200`, { string: true, }), public_repos: millify(user.repositories.totalCount), @@ -58,8 +76,7 @@ async function getData(username: string): Promise { total_prs: millify(user.pullRequests.totalCount), total_prs_merged: millify(user.mergedPullRequests.totalCount), total_commits: millify( - user.restrictedContributionsCount + - user.totalCommitContributions + user.restrictedContributionsCount + user.totalCommitContributions ), total_review: millify(user.totalPullRequestReviewContributions), total_discussion_answered: millify(user.discussionAnswered.totalCount), @@ -70,4 +87,5 @@ async function getData(username: string): Promise { return output; } +export { GetData, getData }; export default getData; diff --git a/src/getRandomToken.ts b/src/getRandomToken.ts deleted file mode 100644 index 431a201..0000000 --- a/src/getRandomToken.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Importing necessary module -import dotenv from "dotenv"; -import { getInput } from "@actions/core"; - -// Loads environment variables from a .env file if present -dotenv.config(); - -/** - * Generates a random token based on the GH_ environment variables. - * - * @param {boolean} bearerHeader Indicates whether to include "Bearer " prefix for authorization header. - * @returns {string} A random token. - */ -function getRandomToken(bearerHeader: boolean): string { - const getEnvs: any = process.env; - let getGhEnv: any = Object.keys(getEnvs).filter((key) => - key.startsWith("GH_") - ); - getGhEnv = getGhEnv.map((key: string) => getEnvs[key]); - const getRandomEnv: string = - getGhEnv[Math.floor(Math.random() * getGhEnv.length)]; - - if (!getRandomEnv) { - // Use GitHub Actions core module to get the token - const getRandomEnv = getInput("github_token"); - - if (!getRandomEnv) { - throw new Error("Could not find github token"); - } - } - - if (bearerHeader) { - return `Bearer ${getRandomEnv}`; - } - - return getRandomEnv; -} - -export default getRandomToken; diff --git a/src/getToken.ts b/src/getToken.ts new file mode 100644 index 0000000..e84e2f7 --- /dev/null +++ b/src/getToken.ts @@ -0,0 +1,39 @@ +import dotenv from "dotenv"; +import { getInput } from "@actions/core"; + +dotenv.config(); + +/** + * Retrieves the GitHub token from the environment variables or GitHub Actions inputs. + * + * @param {boolean} bearerHeader - Flag indicating whether to return the token with 'Bearer' prefix. + * @returns {string} - The GitHub token. + */ +function getToken(bearerHeader: boolean): string { +const getEnvirontment: any = process.env; + let getGHEnvirontment: any = Object.keys(getEnvirontment).filter((key) => + key.startsWith("GH_") + ); + getGHEnvirontment = getGHEnvirontment.map((key: string) => getEnvirontment[key]); + + // Select a random GitHub environment variable + const getGHToken: string = + getGHEnvirontment[Math.floor(Math.random() * getGHEnvirontment.length)]; + + // If no GitHub environment variable is found, get the token from GitHub Actions inputs + if (!getGHToken) { + const getGHToken = getInput("github_token"); + + if (!getGHToken) { + throw new Error("Could not find github token"); + } + } + + if (bearerHeader) { + return `Bearer ${getGHToken}`; + } + + return getGHToken; +} + +export default getToken; diff --git a/src/icons.ts b/src/icons.ts index 020c8d7..cb327c4 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,5 +1,21 @@ -// Represents the available icons for GitHub stats cards -export type Icons = { +/** + * Type representing an object storing SVG icons for various categories. + * + * @typedef {Object} Icons + * @property {string} repository - SVG icon for the repository category. + * @property {string} star - SVG icon for the star category. + * @property {string} fork - SVG icon for the fork category. + * @property {string} commit - SVG icon for the commit category. + * @property {string} review - SVG icon for the review category. + * @property {string} pull_request - SVG icon for the pull request category. + * @property {string} pull_request_merged - SVG icon for the merged pull request category. + * @property {string} issue - SVG icon for the issue category. + * @property {string} issue_closed - SVG icon for the closed issue category. + * @property {string} discussion_started - SVG icon for the started discussion category. + * @property {string} discussion_answered - SVG icon for the answered discussion category. + * @property {string} contributed_to - SVG icon for the contributed to category. + */ +type Icons = { repository: string; star: string; fork: string; @@ -14,8 +30,12 @@ export type Icons = { contributed_to: string; }; -// Object containing various SVG path data for different GitHub-related icons -export const icons = { +/** + * Icons object stores SVG icons for various categories. + * + * @type {Icons} + */ +const icons: Icons = { repository: ``, star: ``, fork: ``, @@ -28,4 +48,7 @@ export const icons = { discussion_started: ``, discussion_answered: ``, contributed_to: ``, -} +}; + +export { Icons, icons }; +export default icons; diff --git a/src/translations.ts b/src/translations.ts index 3fd0379..e5dd93b 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -1,5 +1,25 @@ -// Represents the structure of localized strings -export type Locales = { +/** + * Type representing for local translation card strings. + * + * @typedef {Object} Locales + * @property {string} titleCard - Title for the GitHub stats card. + * @property {string} [followersText] - Text for followers count. + * @property {string} [followingText] - Text for following count. + * @property {string} [totalReposText] - Text for total repository count. + * @property {string} [starsCountText] - Text for stars count. + * @property {string} [forksCountText] - Text for forks count. + * @property {string} [commitsCountText] - Text for commits count. + * @property {string} [totalPRText] - Text for total pull requests count. + * @property {string} [totalPRMergedText] - Text for total merged pull requests count. + * @property {string} [totalPRReviewedText] - Text for total pull requests reviewed count. + * @property {string} [totalIssuesText] - Text for total issues count. + * @property {string} [totalIssuesClosedText] - Text for total closed issues count. + * @property {string} [totalDiscussionStartedText] - Text for total discussions started count. + * @property {string} [totalDiscussionAnsweredText] - Text for total discussions answered count. + * @property {string} [contributedToText] - Text for contributed repositories (last year). + * @property {boolean|string} rtlDirection - Flag indicating right-to-left direction or a string specifying direction. + */ +type Locales = { [key: string]: { titleCard: string; followersText?: string; @@ -20,8 +40,12 @@ export type Locales = { }; }; -// Object containing localized strings for different languages -export const locales: Locales = { +/** + * Localization strings for different languages. + * + * @type {Locales} + */ +const locales: Locales = { en: { titleCard: "{name}'s GitHub Stats", followersText: "Followers", @@ -221,3 +245,6 @@ export const locales: Locales = { rtlDirection: false, }, } + +export { Locales, locales }; +export default locales; diff --git a/tests/getToken.test.ts b/tests/getToken.test.ts new file mode 100644 index 0000000..23cf956 --- /dev/null +++ b/tests/getToken.test.ts @@ -0,0 +1,58 @@ +import getToken from "../src/getToken"; +import * as core from "@actions/core"; + +jest.mock("dotenv"); +jest.mock("@actions/core"); + +type MockedGetInput = jest.MockedFunction; + +const mockedGetInput = core.getInput as MockedGetInput; + + +describe("getToken function", () => { + const originalEnv = process.env; + beforeEach(() => { + process.env = { ...originalEnv }; + }); + afterAll(() => { + process.env = originalEnv; + }); + + it("should return an individual token without Bearer prefix", () => { + process.env.GH_TOKEN = "ghp_token"; + + const token = getToken(false); + + expect(token).toEqual("ghp_token"); + }); + + it("should return an individual token with Bearer prefix", () => { + process.env.GH_TOKEN = "ghp_token"; + + const token = getToken(true); + + expect(token).toEqual("Bearer ghp_token"); + }); + + it("should return a GitHub Actions bot token without Bearer prefix", () => { + mockedGetInput.mockReturnValue("GitHubActionsBotToken"); + + const token = getToken(false); + + expect(token).not.toEqual("GitHubActionsBotToken"); + }); + + it("should return a GitHub Actions bot token with Bearer prefix", () => { + mockedGetInput.mockReturnValue("GitHubActionsBotToken"); + + const token = getToken(true); + + expect(token).not.toEqual("Bearer GitHubActionsBotToken"); + }); + + it("should throw an error if no tokens are available", () => { + mockedGetInput.mockReturnValue(""); + + expect(() => getToken(false)).toThrowError("Could not find github token"); + }); +}); diff --git a/tests/randomToken.test.ts b/tests/randomToken.test.ts deleted file mode 100644 index f486e62..0000000 --- a/tests/randomToken.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import getRandomToken from "../src/getRandomToken"; - -// Mock the dependencies -jest.mock("dotenv"); -jest.mock("@actions/core"); - -describe("getRandomToken function", () => { - // Mock process.env values - const originalEnv = process.env; - beforeEach(() => { - process.env = { ...originalEnv }; - }); - afterAll(() => { - process.env = originalEnv; - }); - - it("should return a random token without Bearer prefix", () => { - // Mock GitHub environment variables - process.env.GH_TOKEN_1 = "token1"; - process.env.GH_TOKEN_2 = "token2"; - - const token = getRandomToken(false); - - expect(token).toEqual(expect.stringMatching(/^token\d$/)); - }); - - it("should return a random token with Bearer prefix", () => { - // Mock GitHub environment variables - process.env.GH_TOKEN_1 = "token1"; - process.env.GH_TOKEN_2 = "token2"; - - const token = getRandomToken(true); - - expect(token).toEqual(expect.stringMatching(/^Bearer token\d$/)); - }); - - it("should throw an error if no tokens are available", () => { - // No GitHub environment variables and no GitHub Actions input - jest.mock("@actions/core", () => ({ - getInput: jest.fn().mockReturnValue(""), - })); - - expect(() => getRandomToken(false)).toThrowError("Could not find github token"); - }); -}); diff --git a/tests/renderAPIStatsCard.test.ts b/tests/renderStatsCard.test.ts similarity index 65% rename from tests/renderAPIStatsCard.test.ts rename to tests/renderStatsCard.test.ts index 9da407e..0bc225b 100644 --- a/tests/renderAPIStatsCard.test.ts +++ b/tests/renderStatsCard.test.ts @@ -1,16 +1,40 @@ import readmeStats from "../api/index"; import getData from "../src/getData"; -import cardStyle from "../src/card"; -import { themes } from "../themes/index"; -import { locales } from "../src/translations"; +import type { User } from "../src/fetcher/stats"; +import card from "../src/card"; +import themes from "../themes/index"; +import locales from "../src/translations"; jest.mock("../src/getData"); jest.mock("../src/card"); -jest.mock("express"); -function valueReturn(value: string): string { - return `${value}`; -} +jest.mock("express", () => ({ + ...jest.requireActual("express"), + __esModule: true, + default: { + Request: jest.fn(), + Response: jest.fn(), + }, +})); + +const exampleUserData: User = { + name: "Fajar", + login: "FajarKim", + avatarUrl: "base64-encoded-image", + repositories: { totalCount: 20 }, + followers: { totalCount: 20 }, + following: { totalCount: 10 }, + openedIssues: { totalCount: 150 }, + closedIssues: { totalCount: 100 }, + pullRequests: { totalCount: 530 }, + mergedPullRequests: { totalCount: 490 }, + discussionStarted: { totalCount: 4 }, + discussionAnswered: { totalCount: 10 }, + repositoriesContributedTo: { totalCount: 12 }, + totalCommitContributions: 1200, + restrictedContributionsCount: 130, + totalPullRequestReviewContributions: 340, +}; describe("Test GitHub Readme Profile API", () => { let mockRequest: any; @@ -28,18 +52,18 @@ describe("Test GitHub Readme Profile API", () => { status: jest.fn(), }; - // Reset mock implementation for each module jest.clearAllMocks(); }); it("should handle request and generate JSON response", async () => { mockRequest.query.username = "FajarKim"; mockRequest.query.format = "json"; + (getData as jest.Mock).mockResolvedValueOnce(exampleUserData); await readmeStats(mockRequest, mockResponse); expect(getData).toHaveBeenCalledWith(mockRequest.query.username); - expect(mockResponse.json).toHaveBeenCalledWith(await getData(mockRequest.query.username)); + expect(mockResponse.json).toHaveBeenCalledWith(exampleUserData); expect(mockResponse.send).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Cache-Control", "s-maxage=3600, stale-while-revalidate"); }); @@ -47,11 +71,12 @@ describe("Test GitHub Readme Profile API", () => { it("should handle request and generate SVG response", async () => { mockRequest.query.username = "FajarKim"; mockRequest.query.format = "svg"; + (getData as jest.Mock).mockResolvedValueOnce(exampleUserData); await readmeStats(mockRequest, mockResponse); expect(getData).toHaveBeenCalledWith(mockRequest.query.username); - expect(cardStyle).toHaveBeenCalledWith(await getData(mockRequest.query.username), expect.any(Object)); + expect(card).toHaveBeenCalledWith(exampleUserData, expect.any(Object)); expect(mockResponse.send).toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Content-Type", "image/svg+xml"); @@ -60,11 +85,12 @@ describe("Test GitHub Readme Profile API", () => { it.each(Object.keys(themes))("should handle request theme '%s' and generate SVG response", async (theme) => { mockRequest.query.username = "FajarKim"; mockRequest.query.theme = theme; + (getData as jest.Mock).mockResolvedValueOnce(exampleUserData); await readmeStats(mockRequest, mockResponse); expect(getData).toHaveBeenCalledWith(mockRequest.query.username); - expect(cardStyle).toHaveBeenCalledWith(await getData(mockRequest.query.username), expect.any(Object)); + expect(card).toHaveBeenCalledWith(exampleUserData, expect.any(Object)); expect(mockResponse.send).toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Content-Type", "image/svg+xml"); @@ -73,11 +99,12 @@ describe("Test GitHub Readme Profile API", () => { it.each(Object.keys(locales))("should handle request locale '%s' and generate SVG response", async (locale) => { mockRequest.query.username = "FajarKim"; mockRequest.query.locale = locale; + (getData as jest.Mock).mockResolvedValueOnce(exampleUserData); await readmeStats(mockRequest, mockResponse); expect(getData).toHaveBeenCalledWith(mockRequest.query.username); - expect(cardStyle).toHaveBeenCalledWith(await getData(mockRequest.query.username), expect.any(Object)); + expect(card).toHaveBeenCalledWith(exampleUserData, expect.any(Object)); expect(mockResponse.send).toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Content-Type", "image/svg+xml"); @@ -86,11 +113,12 @@ describe("Test GitHub Readme Profile API", () => { it("should handle request hide stats and generate SVG response", async () => { mockRequest.query.username = "FajarKim"; mockRequest.query.hide = "repos,forks,prs_merged"; + (getData as jest.Mock).mockResolvedValueOnce(exampleUserData); await readmeStats(mockRequest, mockResponse); expect(getData).toHaveBeenCalledWith(mockRequest.query.username); - expect(cardStyle).toHaveBeenCalledWith(await getData(mockRequest.query.username), expect.any(Object)); + expect(card).toHaveBeenCalledWith(exampleUserData, expect.any(Object)); expect(mockResponse.send).toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Content-Type", "image/svg+xml"); @@ -99,21 +127,24 @@ describe("Test GitHub Readme Profile API", () => { it("should handle request show stats and generate SVG response", async () => { mockRequest.query.username = "FajarKim"; mockRequest.query.show = "reviews,issues_closed"; + (getData as jest.Mock).mockResolvedValueOnce(exampleUserData); await readmeStats(mockRequest, mockResponse); expect(getData).toHaveBeenCalledWith(mockRequest.query.username); - expect(cardStyle).toHaveBeenCalledWith(await getData(mockRequest.query.username), expect.any(Object)); + expect(card).toHaveBeenCalledWith(exampleUserData, expect.any(Object)); expect(mockResponse.send).toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Content-Type", "image/svg+xml"); }); - it("should handle invalid username and send error response", async () => { + it("should handle invalid missing username and send error response", async () => { + mockRequest.query.username = undefined; + await readmeStats(mockRequest, mockResponse); expect(getData).not.toHaveBeenCalledWith(); - expect(cardStyle).not.toHaveBeenCalledWith(); + expect(card).not.toHaveBeenCalledWith(); expect(mockResponse.send).toHaveBeenCalled(); expect(mockResponse.json).not.toHaveBeenCalled(); expect(mockResponse.setHeader).toHaveBeenCalledWith("Cache-Control", "s-maxage=3600, stale-while-revalidate"); diff --git a/themes/index.ts b/themes/index.ts index 8efa3da..b4dd6cf 100644 --- a/themes/index.ts +++ b/themes/index.ts @@ -1,5 +1,16 @@ -// Represents the available themes for GitHub stats cards -export type Themes = { +/** + * Type representing a theme configuration. + * + * @typedef {Object} Theme + * @property {string|undefined} titleColor - Color for the title text. + * @property {string|undefined} textColor - Color for the main text. + * @property {string|undefined} iconColor - Color for icons. + * @property {string|undefined} borderColor - Color for borders. + * @property {string|undefined} bgColor - Background color. + * @property {string|undefined} strokeColor - Color for strokes. + * @property {string|undefined} usernameColor - Color for the username. + */ +type Themes = { [key: string]: { title_color?: string; text_color?: string; @@ -11,8 +22,12 @@ export type Themes = { }; }; -// Object containing various predefined themes for GitHub stats cards -export const themes: Themes = { +/** + * Themes object stores theme collection for the card. + * + * @type {Themes} + */ +const themes: Themes = { // Solid themes default: { title_color: "2f80ed", @@ -535,4 +550,7 @@ export const themes: Themes = { icon_color: "EFFA4B", bg_color: "50,4A1133,0B1133", }, -} +}; + +export { Themes, themes }; +export default themes; diff --git a/withexpress.ts b/withexpress.ts index fbde7a8..7f409ec 100644 --- a/withexpress.ts +++ b/withexpress.ts @@ -5,7 +5,7 @@ import readmeStats from "./api/index"; dotenv.config(); const app = express(); -app.use("/api", readmeStats); +app.use("/", readmeStats); const port: number = Number(process.env.PORT) || 3000; From b40f9b35e89f2d0d0d46a1ab19f483b6511f8028 Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sat, 6 Jan 2024 09:26:18 +0700 Subject: [PATCH 25/33] fix: bugs check valid gradient color (#225) --- src/common/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/utils.ts b/src/common/utils.ts index 48ae38a..61ea7e2 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -4,7 +4,7 @@ * @param {any} hexColor - The input value to check for valid hexadecimal color. * @returns {boolean} - True if the input is a valid hexadecimal color, false otherwise. */ -function isValidHexColor(hexColor: any): boolean { +function isValidHexColor(hexColor: string): boolean { 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); @@ -16,8 +16,8 @@ function isValidHexColor(hexColor: any): boolean { * @param {string[]} hexColors - The array of hexadecimal colors to check for a valid gradient. * @returns {boolean} - True if the array represents a valid gradient, false otherwise. */ -function isValidGradient(hexColors: string[]): boolean { - const colors = [hexColors]; +function isValidGradient(hexColors: string): boolean { + const colors = hexColors.split(","); for (let i = 1; i < colors.length; i++) { if (!isValidHexColor(colors[i])) { return false; From d0ebb68b1efcae3df4ef281d3782b597485ac39c Mon Sep 17 00:00:00 2001 From: Rangga Fajar Oktariansyah <86386385+FajarKim@users.noreply.github.com> Date: Sat, 6 Jan 2024 09:30:03 +0700 Subject: [PATCH 26/33] tests: added card and utils performance test (#226) --- tests/card.test.ts | 138 ++++++++++++++++++++++++++++++++++++++++++++ tests/utils.test.ts | 41 +++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 tests/card.test.ts create mode 100644 tests/utils.test.ts diff --git a/tests/card.test.ts b/tests/card.test.ts new file mode 100644 index 0000000..9b09cab --- /dev/null +++ b/tests/card.test.ts @@ -0,0 +1,138 @@ +import card from "../src/card"; +import locales from "../src/translations"; + +describe("Test card function", () => { + const mockData = { + username: "FajarKim", + name: "Rangga Fajar Oktariansyah", + picture: "avatar", + public_repos: 20, + followers: 20, + following: 10, + total_stars: 140, + total_forks: 90, + total_issues: 150, + total_closed_issues: 100, + total_prs: 530, + total_prs_merged: 490, + total_commits: 1330, + total_review: 340, + total_discussion_answered: 4, + total_discussion_started: 10, + total_contributed_to: 12, + }; + + const mockUiConfig = { + Locale: "en", + Format: "svg", + titleColor: "2f80ed", + textColor: "434d58", + iconColor: "4c71f2", + borderColor: "e4e2e2", + bgColor: "ffffff", + strokeColor: "e4e2e2", + usernameColor: "2f80ed", + borderRadius: 5, + borderWidth: 1, + hideBorder: false, + hideStroke: false, + disabledAnimations: false, + showItems: "reviews,issues_closed", + hiddenItems: "forks,commits", + }; + + it("should generate SVG markup with default values", () => { + const svgMarkup = card(mockData, mockUiConfig); + + expect(svgMarkup).toContain("