Compare commits
283 Commits
server-202
...
github-oau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6e31d7f32 | ||
|
|
3aadb79325 | ||
|
|
b8412fd80b | ||
|
|
345188e34b | ||
|
|
a92dc72ff5 | ||
|
|
bf469f10df | ||
|
|
994e752fd1 | ||
|
|
f6fd8eac4a | ||
|
|
c41d45100e | ||
|
|
e66b266800 | ||
|
|
1ac7ccc231 | ||
|
|
d728749886 | ||
|
|
354fb7db99 | ||
|
|
961e13b229 | ||
|
|
6bb62e4c0b | ||
|
|
721d0142ff | ||
|
|
13a53f123f | ||
|
|
ca63f21113 | ||
|
|
042ae1c45f | ||
|
|
2f52b1617d | ||
|
|
e91da33016 | ||
|
|
a76df09c35 | ||
|
|
70874e2d5b | ||
|
|
68dbf71d42 | ||
|
|
f4bddb9964 | ||
|
|
cb52deec1c | ||
|
|
047b14b52a | ||
|
|
dfb68efffb | ||
|
|
8284545e22 | ||
|
|
8a1c69ead6 | ||
|
|
1b871a97b4 | ||
|
|
0342a3d7c6 | ||
|
|
77871a9f7b | ||
|
|
15be262ba5 | ||
|
|
05fe731290 | ||
|
|
58310f7363 | ||
|
|
570c2750e2 | ||
|
|
62af78c488 | ||
|
|
c48cd071fe | ||
|
|
a9c9e7d679 | ||
|
|
4a47b9a364 | ||
|
|
901dd7b9b6 | ||
|
|
6dff73065a | ||
|
|
a5f803ff2b | ||
|
|
9780da024e | ||
|
|
12b5e8891f | ||
|
|
5472c733a6 | ||
|
|
fcab8a52dc | ||
|
|
a111e9cba8 | ||
|
|
7a7cda6d4b | ||
|
|
76ca283775 | ||
|
|
b02471ff42 | ||
|
|
7b9d1d340b | ||
|
|
48faded3f7 | ||
|
|
be3fa207b5 | ||
|
|
2624b0fb00 | ||
|
|
b0b986f31f | ||
|
|
61139bbccb | ||
|
|
c5ee05c3fd | ||
|
|
c8fcee3bff | ||
|
|
54844c0892 | ||
|
|
22995e4e35 | ||
|
|
92772de68e | ||
|
|
1aea78a5d0 | ||
|
|
6919b0a49c | ||
|
|
8fd54b1b8d | ||
|
|
779c1ffaad | ||
|
|
e8c78d55b3 | ||
|
|
1c36dd2bd0 | ||
|
|
45def37a0d | ||
|
|
8c3b20b5b8 | ||
|
|
dd63a0b71f | ||
|
|
96a13b8749 | ||
|
|
f28ba52562 | ||
|
|
9a78f0a0c2 | ||
|
|
11bf09bf0f | ||
|
|
44a3e9abb6 | ||
|
|
66dbcb5ca6 | ||
|
|
dabc907fc5 | ||
|
|
71bbcd527b | ||
|
|
70947e1ff0 | ||
|
|
48b7f70ef2 | ||
|
|
96c27c2627 | ||
|
|
17beba4ebf | ||
|
|
95f6611837 | ||
|
|
eb7352e5ae | ||
|
|
c81a1c6196 | ||
|
|
64c6533849 | ||
|
|
8468ddbb07 | ||
|
|
169d51412a | ||
|
|
51e70bae8d | ||
|
|
8be2da17ed | ||
|
|
8e0788ccdc | ||
|
|
36d80af111 | ||
|
|
5b583cc9ef | ||
|
|
1444b5624c | ||
|
|
45a9c90782 | ||
|
|
a4c93c56e7 | ||
|
|
f4781804f6 | ||
|
|
21742a50b4 | ||
|
|
2cb52494c3 | ||
|
|
fbef4a5d5d | ||
|
|
64b747605c | ||
|
|
ea071535e3 | ||
|
|
3b5f915443 | ||
|
|
8b17c7b175 | ||
|
|
ec028c5158 | ||
|
|
b901b2b216 | ||
|
|
ff8f4432b3 | ||
|
|
ed07b61e7d | ||
|
|
087a824a85 | ||
|
|
e18de8c8be | ||
|
|
63fd51b428 | ||
|
|
37e83641ab | ||
|
|
2e59a7a4cc | ||
|
|
fcd402900e | ||
|
|
408e4065e1 | ||
|
|
6af81a70dd | ||
|
|
95b16f2981 | ||
|
|
424e424171 | ||
|
|
017f2e4f25 | ||
|
|
e10c0c2c03 | ||
|
|
8e926953ab | ||
|
|
a3df838d30 | ||
|
|
f64580c243 | ||
|
|
5cdb8102d8 | ||
|
|
435678f14c | ||
|
|
a08b523b4f | ||
|
|
d696d3a4fb | ||
|
|
58dcd8b1d2 | ||
|
|
0acda7cc30 | ||
|
|
9c0a3a5af7 | ||
|
|
9e61513497 | ||
|
|
a74401b99a | ||
|
|
b7ec6c0cb3 | ||
|
|
bd6f4ee146 | ||
|
|
1e72d5e905 | ||
|
|
96a7f36e08 | ||
|
|
6bd739a1a8 | ||
|
|
13a9421f3f | ||
|
|
f859821876 | ||
|
|
f8ff38c791 | ||
|
|
377bcb71ab | ||
|
|
6d4418a52e | ||
|
|
77d0783a76 | ||
|
|
7aecc423f8 | ||
|
|
ad7c959987 | ||
|
|
cf3c166bc7 | ||
|
|
f4dabf6408 | ||
|
|
2fbfe4f8cf | ||
|
|
2e9bbf696a | ||
|
|
42ba150abd | ||
|
|
3cd5e771b6 | ||
|
|
1e19b45063 | ||
|
|
1a1e5451ec | ||
|
|
89392037e3 | ||
|
|
c1ed507217 | ||
|
|
24fc1e164e | ||
|
|
da3c5f782a | ||
|
|
44179bf193 | ||
|
|
28f8403491 | ||
|
|
739de0aa85 | ||
|
|
c64d6ee19f | ||
|
|
d476857327 | ||
|
|
faa2bf973f | ||
|
|
75d5a13235 | ||
|
|
46e1797994 | ||
|
|
e6f2cfd376 | ||
|
|
8983c39f74 | ||
|
|
5f9e8deafc | ||
|
|
30e46a69a7 | ||
|
|
660d832983 | ||
|
|
5aeb74a8a5 | ||
|
|
9c9fd41658 | ||
|
|
2ff8e0b3f4 | ||
|
|
bf6e2ff35d | ||
|
|
e12d691a09 | ||
|
|
7c8c7ac9e9 | ||
|
|
74545557d0 | ||
|
|
c2cec3c266 | ||
|
|
07168fe35b | ||
|
|
686aaf2843 | ||
|
|
3cedd46287 | ||
|
|
72c6f11f3a | ||
|
|
935cf28b39 | ||
|
|
53c643dc1a | ||
|
|
5184dc7378 | ||
|
|
5dd1c1984c | ||
|
|
45d54ef1dd | ||
|
|
6d4ed3ce9d | ||
|
|
8a030e6fb9 | ||
|
|
b485060df4 | ||
|
|
ba0a46599f | ||
|
|
435b14940d | ||
|
|
23c0406bed | ||
|
|
23678fe2f5 | ||
|
|
90831e61ac | ||
|
|
d4ce24b106 | ||
|
|
42e883d728 | ||
|
|
7ec814c7ab | ||
|
|
72b6fa3778 | ||
|
|
49434e3a13 | ||
|
|
670ee76948 | ||
|
|
f8e65e7441 | ||
|
|
5d33a990d6 | ||
|
|
5f46e6fe92 | ||
|
|
ba61107c04 | ||
|
|
f9a44ced8b | ||
|
|
afbc487faa | ||
|
|
d2b966ea42 | ||
|
|
384c57eb47 | ||
|
|
6aae41e1c3 | ||
|
|
08b8153478 | ||
|
|
856c71c1da | ||
|
|
2dfe990e08 | ||
|
|
af9b309d9a | ||
|
|
cf705e77ec | ||
|
|
d4cfddd896 | ||
|
|
8bdc80c988 | ||
|
|
5850e1f10a | ||
|
|
ba314f6955 | ||
|
|
6a4f983e7f | ||
|
|
5ca4089311 | ||
|
|
08e288998c | ||
|
|
218e5b880d | ||
|
|
ae766bbf85 | ||
|
|
4014c50574 | ||
|
|
9527d05819 | ||
|
|
acdbe7e9a5 | ||
|
|
68fb0b7bab | ||
|
|
8f588179e1 | ||
|
|
f4d36d1d21 | ||
|
|
f657cbb7ab | ||
|
|
5fadcf36be | ||
|
|
f19014f112 | ||
|
|
8d7d1b1e49 | ||
|
|
29b8a5f1c3 | ||
|
|
2055114e06 | ||
|
|
a258e27309 | ||
|
|
6e02f47c3f | ||
|
|
861439ffc0 | ||
|
|
3cccb272ac | ||
|
|
6a7b2e3620 | ||
|
|
de019c2e52 | ||
|
|
5eb0f35009 | ||
|
|
956f2e0584 | ||
|
|
918a41672f | ||
|
|
54b0e10675 | ||
|
|
df4315258f | ||
|
|
b9c5d18d4f | ||
|
|
1cfdf94a6b | ||
|
|
2a92c6985e | ||
|
|
4f0acc3f5c | ||
|
|
bde23cfad8 | ||
|
|
e13bcd7dd4 | ||
|
|
c2447c4f2e | ||
|
|
4ae682308e | ||
|
|
ec3e985e6b | ||
|
|
6ac2bd6e46 | ||
|
|
5756196e08 | ||
|
|
6c7ae240b8 | ||
|
|
705fea0939 | ||
|
|
a6b4507e7c | ||
|
|
b0384f438c | ||
|
|
300b317be7 | ||
|
|
e3f0bf33ea | ||
|
|
3246f83e11 | ||
|
|
e8fa269f91 | ||
|
|
103c647d33 | ||
|
|
f7f6d911ca | ||
|
|
fd7adf1f17 | ||
|
|
56234b7b6f | ||
|
|
aaa4d8bfb8 | ||
|
|
a2d213778f | ||
|
|
6a48dd78a4 | ||
|
|
e0f0b1d88f | ||
|
|
03763a9c40 | ||
|
|
a3d85318f5 | ||
|
|
81c7d377d2 | ||
|
|
42a1b82d14 | ||
|
|
984be9a214 | ||
|
|
830bb15b58 | ||
|
|
4fa51257b2 |
@@ -96,23 +96,14 @@ package_steps: &package_steps
|
||||
set +e
|
||||
export NVM_DIR="/opt/circleci/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
nvm install v12
|
||||
nvm use v12
|
||||
nvm install v14
|
||||
nvm use v14
|
||||
npm install -g npm
|
||||
|
||||
# Run the package tests on each currently supported node version. See:
|
||||
# https://github.com/badges/shields/blob/master/badge-maker/README.md#node-version-support
|
||||
# https://nodejs.org/en/about/releases/
|
||||
|
||||
- run:
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/badge-maker/v10/results.xml
|
||||
NODE_VERSION: v10
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
name: Run package tests on Node 10
|
||||
command: scripts/run_package_tests.sh
|
||||
|
||||
- run:
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
@@ -131,39 +122,48 @@ package_steps: &package_steps
|
||||
name: Run package tests on Node 14
|
||||
command: scripts/run_package_tests.sh
|
||||
|
||||
- run:
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/badge-maker/v16/results.xml
|
||||
NODE_VERSION: v16
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
name: Run package tests on Node 16
|
||||
command: scripts/run_package_tests.sh
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
jobs:
|
||||
main:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
- image: circleci/node:14
|
||||
|
||||
<<: *main_steps
|
||||
|
||||
main@node-14:
|
||||
main@node-16:
|
||||
docker:
|
||||
- image: circleci/node:14
|
||||
- image: circleci/node:16
|
||||
|
||||
<<: *main_steps
|
||||
|
||||
integration:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
- image: circleci/node:14
|
||||
- image: redis
|
||||
|
||||
<<: *integration_steps
|
||||
|
||||
integration@node-14:
|
||||
integration@node-16:
|
||||
docker:
|
||||
- image: circleci/node:14
|
||||
- image: circleci/node:16
|
||||
- image: redis
|
||||
|
||||
<<: *integration_steps
|
||||
|
||||
danger:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
- image: circleci/node:14
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
|
||||
frontend:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
- image: circleci/node:14
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
@@ -224,19 +224,19 @@ jobs:
|
||||
|
||||
services:
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
- image: circleci/node:14
|
||||
|
||||
<<: *services_steps
|
||||
|
||||
services@node-14:
|
||||
services@node-16:
|
||||
docker:
|
||||
- image: circleci/node:14
|
||||
- image: circleci/node:16
|
||||
|
||||
<<: *services_steps
|
||||
|
||||
e2e:
|
||||
docker:
|
||||
- image: cypress/base:12
|
||||
- image: cypress/base:14.16.0
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
@@ -285,11 +285,11 @@ workflows:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- main@node-14:
|
||||
- main@node-16:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- integration@node-14:
|
||||
- integration@node-16:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
@@ -307,7 +307,7 @@ workflows:
|
||||
ignore:
|
||||
- master
|
||||
- gh-pages
|
||||
- services@node-14:
|
||||
- services@node-16:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
version: 1
|
||||
update_configs:
|
||||
# shields.io dependencies
|
||||
- package_manager: 'javascript'
|
||||
directory: '/'
|
||||
update_schedule: 'weekly'
|
||||
automerged_updates:
|
||||
- match:
|
||||
dependency_name: 'chai*'
|
||||
update_type: 'semver:minor'
|
||||
- match:
|
||||
dependency_name: 'cypress'
|
||||
update_type: 'semver:minor'
|
||||
- match:
|
||||
dependency_name: 'eslint*'
|
||||
update_type: 'semver:minor'
|
||||
- match:
|
||||
dependency_name: 'mocha*'
|
||||
update_type: 'semver:minor'
|
||||
- match:
|
||||
dependency_name: 'sazerac'
|
||||
update_type: 'semver:minor'
|
||||
- match:
|
||||
dependency_name: 'sinon*'
|
||||
update_type: 'semver:minor'
|
||||
- match:
|
||||
dependency_name: 'snap-shot-it'
|
||||
update_type: 'semver:minor'
|
||||
|
||||
# badge-maker package dependencies
|
||||
- package_manager: 'javascript'
|
||||
directory: '/badge-maker'
|
||||
update_schedule: 'weekly'
|
||||
|
||||
# approve-bot package dependencies
|
||||
- package_manager: 'javascript'
|
||||
directory: '.github/actions/approve-bot'
|
||||
update_schedule: 'weekly'
|
||||
@@ -2,6 +2,6 @@
|
||||
/build
|
||||
/coverage
|
||||
/__snapshots__
|
||||
/public
|
||||
public
|
||||
badge-maker/node_modules/
|
||||
!.github/
|
||||
|
||||
@@ -136,7 +136,6 @@ rules:
|
||||
# Shields additions.
|
||||
no-var: 'error'
|
||||
prefer-const: 'error'
|
||||
strict: 'error'
|
||||
arrow-body-style: ['error', 'as-needed']
|
||||
no-extension-in-require/main: 'error'
|
||||
object-shorthand: ['error', 'properties']
|
||||
|
||||
29
.github/ISSUE_TEMPLATE/1_Bug_report.md
vendored
29
.github/ISSUE_TEMPLATE/1_Bug_report.md
vendored
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: 🐛 Bug Report
|
||||
about: Report errors and problems
|
||||
labels: 'question'
|
||||
---
|
||||
|
||||
Are you experiencing an issue with...
|
||||
|
||||
- [ ] [shields.io](https://shields.io/#/)
|
||||
- [ ] My own instance
|
||||
- [ ] [badge-maker NPM package](https://www.npmjs.com/package/badge-maker)
|
||||
|
||||
:beetle: **Description**
|
||||
|
||||
<!-- A clear and concise description of the problem. -->
|
||||
|
||||
:link: **Link to the badge**
|
||||
|
||||
<!--
|
||||
If you are reporting a problem with a specific badge on shields.io,
|
||||
provide a link to a badge demonstrating the error
|
||||
-->
|
||||
|
||||
:bulb: **Possible Solution**
|
||||
|
||||
<!--- Optional: only if you have suggestions on a fix/reason for the bug -->
|
||||
|
||||
<!-- Love Shields? Please consider donating $10 to sustain our activities:
|
||||
👉 https://opencollective.com/shields -->
|
||||
44
.github/ISSUE_TEMPLATE/1_Bug_report.yml
vendored
Normal file
44
.github/ISSUE_TEMPLATE/1_Bug_report.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: '🐛 Bug Report'
|
||||
description: Report errors and problems
|
||||
labels: [question]
|
||||
body:
|
||||
- type: dropdown
|
||||
id: product
|
||||
attributes:
|
||||
label: Are you experiencing an issue with...
|
||||
options:
|
||||
- shields.io
|
||||
- My own instance of shields
|
||||
- badge-maker NPM package
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: '🐞 Description'
|
||||
description: A clear and concise description of the problem.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: link
|
||||
attributes:
|
||||
label: '🔗 Link to the badge'
|
||||
description: If you are reporting a problem with a specific badge on shields.io, provide a link to a badge demonstrating the error
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: possible-solution
|
||||
attributes:
|
||||
label: '💡 Possible Solution'
|
||||
description: 'Optional: only if you have suggestions on a fix/reason for the bug'
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## :heart: Love Shields?
|
||||
Please consider donating $10 to sustain our activities: [https://opencollective.com/shields](https://opencollective.com/shields)
|
||||
170
.github/actions/approve-bot/package-lock.json
generated
vendored
170
.github/actions/approve-bot/package-lock.json
generated
vendored
@@ -1,170 +0,0 @@
|
||||
{
|
||||
"name": "approve-bot",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.3.0.tgz",
|
||||
"integrity": "sha512-xxtX0Cwdhb8LcgatfJkokqT8KzPvcIbwL9xpLU09nOwBzaStbfm0dNncsP0M4us+EpoPdWy7vbzU5vSOH7K6pg=="
|
||||
},
|
||||
"@actions/github": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.0.tgz",
|
||||
"integrity": "sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@octokit/core": "^3.4.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.13.3",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
|
||||
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
|
||||
"requires": {
|
||||
"tunnel": "0.0.6"
|
||||
}
|
||||
},
|
||||
"@octokit/auth-token": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz",
|
||||
"integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz",
|
||||
"integrity": "sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.4.12",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/endpoint": {
|
||||
"version": "6.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
|
||||
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/graphql": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.2.tgz",
|
||||
"integrity": "sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q==",
|
||||
"requires": {
|
||||
"@octokit/request": "^5.3.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz",
|
||||
"integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz",
|
||||
"integrity": "sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.1.1.tgz",
|
||||
"integrity": "sha512-u4zy0rVA8darm/AYsIeWkRalhQR99qPL1D/EXHejV2yaECMdHfxXiTXtba8NMBSajOJe8+C9g+EqMKSvysx0dg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.14.1",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz",
|
||||
"integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.0.0",
|
||||
"@octokit/types": "^6.7.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/request-error": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz",
|
||||
"integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz",
|
||||
"integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
|
||||
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
|
||||
},
|
||||
"deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,10 +59,4 @@ function isPointlessVersionBump(body) {
|
||||
return allChangelogLinesAreVersionBump(changelogLines)
|
||||
}
|
||||
|
||||
function shouldAutoMerge(body) {
|
||||
return body.includes(
|
||||
'If all status checks pass Dependabot will automatically merge this pull request'
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = { isPointlessVersionBump, shouldAutoMerge }
|
||||
module.exports = { isPointlessVersionBump }
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const { isPointlessVersionBump, shouldAutoMerge } = require('./helpers')
|
||||
const { isPointlessVersionBump } = require('./helpers')
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
@@ -27,24 +27,6 @@ async function run() {
|
||||
state: 'closed',
|
||||
})
|
||||
|
||||
core.debug(`Done.`)
|
||||
} else if (shouldAutoMerge(pr.body)) {
|
||||
core.debug(`Adding label to pull request #${pr.number}`)
|
||||
await client.rest.issues.addLabels({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
labels: ['squash when passing'],
|
||||
})
|
||||
|
||||
core.debug(`Creating approving review for pull request #${pr.number}`)
|
||||
await client.rest.pulls.createReview({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
pull_number: pr.number,
|
||||
event: 'APPROVE',
|
||||
})
|
||||
|
||||
core.debug(`Done.`)
|
||||
}
|
||||
}
|
||||
358
.github/actions/close-bot/package-lock.json
generated
vendored
Normal file
358
.github/actions/close-bot/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
{
|
||||
"name": "close-bot",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "close-bot",
|
||||
"version": "0.0.0",
|
||||
"license": "CC0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.5.0",
|
||||
"@actions/github": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
|
||||
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.0.tgz",
|
||||
"integrity": "sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@octokit/core": "^3.4.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.13.3",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
|
||||
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
|
||||
"dependencies": {
|
||||
"tunnel": "0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz",
|
||||
"integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz",
|
||||
"integrity": "sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.4.12",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "6.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
|
||||
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.2.tgz",
|
||||
"integrity": "sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^5.3.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz",
|
||||
"integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "2.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz",
|
||||
"integrity": "sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.1.1.tgz",
|
||||
"integrity": "sha512-u4zy0rVA8darm/AYsIeWkRalhQR99qPL1D/EXHejV2yaECMdHfxXiTXtba8NMBSajOJe8+C9g+EqMKSvysx0dg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.14.1",
|
||||
"deprecation": "^2.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "5.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz",
|
||||
"integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.0.0",
|
||||
"@octokit/types": "^6.7.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz",
|
||||
"integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz",
|
||||
"integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
|
||||
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
|
||||
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
|
||||
},
|
||||
"@actions/github": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.0.tgz",
|
||||
"integrity": "sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@octokit/core": "^3.4.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.13.3",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
|
||||
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
|
||||
"requires": {
|
||||
"tunnel": "0.0.6"
|
||||
}
|
||||
},
|
||||
"@octokit/auth-token": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz",
|
||||
"integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz",
|
||||
"integrity": "sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.4.12",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/endpoint": {
|
||||
"version": "6.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
|
||||
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/graphql": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.2.tgz",
|
||||
"integrity": "sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q==",
|
||||
"requires": {
|
||||
"@octokit/request": "^5.3.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz",
|
||||
"integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz",
|
||||
"integrity": "sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.1.1.tgz",
|
||||
"integrity": "sha512-u4zy0rVA8darm/AYsIeWkRalhQR99qPL1D/EXHejV2yaECMdHfxXiTXtba8NMBSajOJe8+C9g+EqMKSvysx0dg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.14.1",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz",
|
||||
"integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.0.0",
|
||||
"@octokit/types": "^6.7.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/request-error": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz",
|
||||
"integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz",
|
||||
"integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
|
||||
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
|
||||
},
|
||||
"deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "approve-bot",
|
||||
"name": "close-bot",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
@@ -10,7 +10,7 @@
|
||||
"author": "chris48s",
|
||||
"license": "CC0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.3.0",
|
||||
"@actions/core": "^1.5.0",
|
||||
"@actions/github": "^5.0.0"
|
||||
}
|
||||
}
|
||||
28
.github/dependabot.yml
vendored
Normal file
28
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
version: 2
|
||||
updates:
|
||||
# shields.io dependencies
|
||||
- package-ecosystem: npm
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 99
|
||||
|
||||
# badge-maker package dependencies
|
||||
- package-ecosystem: npm
|
||||
directory: '/badge-maker'
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 99
|
||||
|
||||
# close-bot package dependencies
|
||||
- package-ecosystem: npm
|
||||
directory: '/.github/actions/close-bot'
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 99
|
||||
@@ -1,16 +1,20 @@
|
||||
name: Auto approve
|
||||
on: pull_request
|
||||
name: Auto close
|
||||
on: pull_request_target
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install action dependencies
|
||||
run: cd .github/actions/approve-bot && npm ci
|
||||
run: cd .github/actions/close-bot && npm ci
|
||||
|
||||
- uses: ./.github/actions/approve-bot
|
||||
- uses: ./.github/actions/close-bot
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
20
.github/workflows/build-docker-image.yml
vendored
Normal file
20
.github/workflows/build-docker-image.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Build Docker Image
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: shieldsio/shields:pr-validation
|
||||
@@ -1,11 +1,11 @@
|
||||
name: Tag Release
|
||||
name: Create Release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
tag-release:
|
||||
create-release:
|
||||
if: |
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action == 'closed' &&
|
||||
@@ -24,8 +24,24 @@ jobs:
|
||||
with:
|
||||
ref: 'master'
|
||||
|
||||
- name: Tag Release
|
||||
- name: Tag release in GitHub
|
||||
uses: tvdias/github-tagger@v0.0.2
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: server-${{ steps.date.outputs.date }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push snapshot release to DockerHub
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: shieldsio/shields:server-${{ steps.date.outputs.date }}
|
||||
28
.github/workflows/publish-docker-next.yml
vendored
Normal file
28
.github/workflows/publish-docker-next.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Build and Publish Next Docker Image
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: shieldsio/shields:next
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -104,7 +104,8 @@ service-definitions.yml
|
||||
!/config/local*.template.yml
|
||||
|
||||
# Gatsby
|
||||
/.cache
|
||||
/frontend/.cache
|
||||
/frontend/public
|
||||
/public
|
||||
|
||||
# Cypress
|
||||
|
||||
@@ -3,4 +3,3 @@ require:
|
||||
- '@babel/polyfill'
|
||||
- '@babel/register'
|
||||
- mocha-yaml-loader
|
||||
- frontend/mocha-ignore-pngs
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"scripts",
|
||||
"coverage",
|
||||
"build",
|
||||
".github"
|
||||
".github",
|
||||
"**/public/"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package.json
|
||||
package-lock.json
|
||||
/__snapshots__
|
||||
/.next
|
||||
/.cache
|
||||
.cache
|
||||
/api-docs
|
||||
/build
|
||||
/public
|
||||
public
|
||||
/coverage
|
||||
private/*.json
|
||||
/.nyc_output
|
||||
|
||||
41
CHANGELOG.md
41
CHANGELOG.md
@@ -4,6 +4,47 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
|
||||
|
||||
---
|
||||
|
||||
## server-2021-09-01
|
||||
|
||||
- use multi-stage build to reduce size of docker images [#6938](https://github.com/badges/shields/issues/6938)
|
||||
- remove disableStrictSsl param from [jenkins] [#6887](https://github.com/badges/shields/issues/6887)
|
||||
- refactor(GitHubCommitActivity): switch to v4/GraphQL API [#6959](https://github.com/badges/shields/issues/6959)
|
||||
- feat: add freecodecamp badge [#6958](https://github.com/badges/shields/issues/6958)
|
||||
- use the right version of NPM in docker build [#6941](https://github.com/badges/shields/issues/6941)
|
||||
- [TwitchExtensionVersion] New badge [#6900](https://github.com/badges/shields/issues/6900)
|
||||
- enforce strict SSL checking for [coverity] [#6886](https://github.com/badges/shields/issues/6886)
|
||||
- Update self hosting docs [#6877](https://github.com/badges/shields/issues/6877)
|
||||
- Support optionalDependencies in [GithubPackageJson] [#6749](https://github.com/badges/shields/issues/6749)
|
||||
- Dependency updates
|
||||
|
||||
## server-2021-08-01
|
||||
|
||||
- use v5 API for [AUR] badges [#6836](https://github.com/badges/shields/issues/6836)
|
||||
- [Sonar] Fix invalid fetch query to sonarqube >=6.6 [#6636](https://github.com/badges/shields/issues/6636)
|
||||
- Delegate discord logo to simple-icons, which matches the current branding [#6764](https://github.com/badges/shields/issues/6764)
|
||||
- Re-apply 'Migrate request to got (part 1)' [#6755](https://github.com/badges/shields/issues/6755)
|
||||
- Delete old deprecated badges [#6756](https://github.com/badges/shields/issues/6756)
|
||||
- Replace opn-cli with open-cli [#6747](https://github.com/badges/shields/issues/6747)
|
||||
- Verify that Node 14 is installed in development [#6748](https://github.com/badges/shields/issues/6748)
|
||||
- Migrate from CommonJS to ESM [#6651](https://github.com/badges/shields/issues/6651)
|
||||
- Add Wikiapiary Extension Badge [WikiapiaryInstalls] [#6678](https://github.com/badges/shields/issues/6678)
|
||||
- deprecate [beerpay] [#6708](https://github.com/badges/shields/issues/6708)
|
||||
- deprecate [microbadger] [#6709](https://github.com/badges/shields/issues/6709)
|
||||
- [npmsioscore] Support npm score [#6630](https://github.com/badges/shields/issues/6630)
|
||||
- Add [Weblate] badges [#6677](https://github.com/badges/shields/issues/6677)
|
||||
- Dependency updates
|
||||
|
||||
## server-2021-07-01
|
||||
|
||||
- improve [MavenCentral], [MavenMetadata], and [GradlePluginPortal] [#6628](https://github.com/badges/shields/issues/6628)
|
||||
- fix: fix regex to match [codecov]'s flags [#6655](https://github.com/badges/shields/issues/6655)
|
||||
- fix usage style [#6638](https://github.com/badges/shields/issues/6638)
|
||||
- update simple-icons to v5 with by-name lookup backwards compatibility [#6591](https://github.com/badges/shields/issues/6591)
|
||||
- [GradlePluginPortal] add gradle plugin portal [#6449](https://github.com/badges/shields/issues/6449)
|
||||
- upgrade some vulnerable packages [#6569](https://github.com/badges/shields/issues/6569)
|
||||
- increase max-age for download and social badges [#6567](https://github.com/badges/shields/issues/6567)
|
||||
- Dependency updates
|
||||
|
||||
## server-2021-06-01
|
||||
|
||||
- Changed creating badges to open a new Window/Tab [#6536](https://github.com/badges/shields/issues/6536)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:12-alpine
|
||||
FROM node:14-alpine AS Builder
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
RUN mkdir /usr/src/app/private
|
||||
@@ -8,6 +8,7 @@ COPY package.json package-lock.json /usr/src/app/
|
||||
# Without the badge-maker package.json and CLI script in place, `npm ci` will fail.
|
||||
COPY badge-maker /usr/src/app/badge-maker/
|
||||
|
||||
RUN npm install -g "npm@>=7"
|
||||
# We need dev deps to build the front end. We don't need Cypress, though.
|
||||
RUN NODE_ENV=development CYPRESS_INSTALL_BINARY=0 npm ci
|
||||
|
||||
@@ -16,9 +17,14 @@ RUN npm run build
|
||||
RUN npm prune --production
|
||||
RUN npm cache clean --force
|
||||
|
||||
# Use multi-stage build to reduce size
|
||||
FROM node:14-alpine
|
||||
# Run the server using production configs.
|
||||
ENV NODE_ENV production
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY --from=Builder /usr/src/app /usr/src/app
|
||||
|
||||
CMD node server
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
43
README.md
43
README.md
@@ -73,7 +73,7 @@ This repo hosts:
|
||||
[Make your own badges!][custom badges]
|
||||
(Quick example: `https://img.shields.io/badge/left-right-f39f37`)
|
||||
|
||||
[custom badges]: http://shields.io/#your-badge
|
||||
[custom badges]: https://shields.io/#your-badge
|
||||
|
||||
### Quickstart
|
||||
|
||||
@@ -96,13 +96,13 @@ You can read a [tutorial on how to add a badge][tutorial].
|
||||
[](https://github.com/badges/shields/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
||||
|
||||
[service-tests]: https://github.com/badges/shields/blob/master/doc/service-tests.md
|
||||
[tutorial]: doc/TUTORIAL.md
|
||||
[contributing]: CONTRIBUTING.md
|
||||
[tutorial]: https://github.com/badges/shields/blob/master/doc/TUTORIAL.md
|
||||
[contributing]: https://github.com/badges/shields/blob/master/CONTRIBUTING.md
|
||||
|
||||
## Development
|
||||
|
||||
1. Install Node 12 or later. You can use the [package manager][] of your choice.
|
||||
Tests need to pass in Node 12 and 14.
|
||||
1. Install Node 14 or later. You can use the [package manager][] of your choice.
|
||||
Tests need to pass in Node 14 and 16.
|
||||
2. Clone this repository.
|
||||
3. Run `npm ci` to install the dependencies.
|
||||
4. Run `npm start` to start the badge server and the frontend dev server.
|
||||
@@ -142,9 +142,9 @@ Daily tests, including a full run of the service tests and overall code coverage
|
||||
[gitpod]: https://www.gitpod.io/
|
||||
[snapshot tests]: https://glebbahmutov.com/blog/snapshot-testing/
|
||||
[prometheus]: https://prometheus.io/
|
||||
[prometheus configuration]: doc/self-hosting.md#prometheus
|
||||
[prometheus configuration]: https://github.com/badges/shields/blob/master/doc/self-hosting.md#prometheus
|
||||
[sentry]: https://sentry.io/
|
||||
[sentry configuration]: doc/self-hosting.md#sentry
|
||||
[sentry configuration]: https://github.com/badges/shields/blob/master/doc/self-hosting.md#sentry
|
||||
[daily-tests]: https://github.com/badges/daily-tests
|
||||
[nodemon]: https://nodemon.io/
|
||||
[nodemon debug]: https://github.com/Microsoft/vscode-recipes/tree/master/nodemon
|
||||
@@ -154,7 +154,22 @@ Daily tests, including a full run of the service tests and overall code coverage
|
||||
|
||||
There is documentation about [hosting your own server][self-hosting].
|
||||
|
||||
[self-hosting]: doc/self-hosting.md
|
||||
[self-hosting]: https://github.com/badges/shields/blob/master/doc/self-hosting.md
|
||||
|
||||
## Related projects
|
||||
|
||||
[](https://awesome.re)
|
||||
|
||||
Status badges are used widely across open-source and private software projects.
|
||||
Academics have studied the "signal" badges provide about software project
|
||||
quality. There are many existing libraries for rendering these badges, and
|
||||
alternatives to the hosted Shields badge service. [awesome-badges][] is a
|
||||
curated collection of such resources.
|
||||
[Contributions][contributing to awesome-badges] may be considered there.
|
||||
(The presence of a project in that collection should not be interpreted as an endorsement nor promotion from the Shields project)
|
||||
|
||||
[awesome-badges]: https://github.com/badges/awesome-badges
|
||||
[contributing to awesome-badges]: https://github.com/badges/awesome-badges/blob/main/CONTRIBUTING.md
|
||||
|
||||
## History
|
||||
|
||||
@@ -179,8 +194,8 @@ You can read more about [the project's inception][thread],
|
||||
[olivierlacan]: https://github.com/olivierlacan
|
||||
[espadrine]: https://github.com/espadrine
|
||||
[old-gh-badges]: https://github.com/badges/gh-badges
|
||||
[motivation]: spec/motivation.md
|
||||
[spec]: spec/SPECIFICATION.md
|
||||
[motivation]: https://github.com/badges/shields/blob/master/spec/motivation.md
|
||||
[spec]: https://github.com/badges/shields/blob/master/spec/SPECIFICATION.md
|
||||
[thread]: https://github.com/h5bp/lazyweb-requests/issues/150
|
||||
|
||||
## Project leaders
|
||||
@@ -207,14 +222,6 @@ Alumni:
|
||||
- [espadrine](https://github.com/espadrine)
|
||||
- [olivierlacan](https://github.com/olivierlacan)
|
||||
|
||||
## Related projects
|
||||
|
||||
- [poser PHP library][poser]
|
||||
- [pybadges python library][pybadges]
|
||||
|
||||
[poser]: https://github.com/badges/poser
|
||||
[pybadges]: https://github.com/google/pybadges
|
||||
|
||||
## License
|
||||
|
||||
All assets and code are under the [CC0 LICENSE](LICENSE) and in the public
|
||||
|
||||
@@ -402,6 +402,126 @@ exports['The badge generator "flat" template badge generation should match snaps
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "flat" template badge generation should match snapshots: black text when the label color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="20"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
|
||||
<stop offset="1" stop-opacity=".1" />
|
||||
</linearGradient>
|
||||
<clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff" /></clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="45" height="20" fill="#f3f3f3" />
|
||||
<rect x="45" width="45" height="20" fill="#000" />
|
||||
<rect width="90" height="20" fill="url(#s)" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="235"
|
||||
y="150"
|
||||
fill="#ccc"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
cactus
|
||||
</text>
|
||||
<text x="235" y="140" transform="scale(.1)" fill="#333" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="665"
|
||||
y="150"
|
||||
fill="#010101"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
grown
|
||||
</text>
|
||||
<text x="665" y="140" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "flat" template badge generation should match snapshots: black text when the message color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="20"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
|
||||
<stop offset="1" stop-opacity=".1" />
|
||||
</linearGradient>
|
||||
<clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff" /></clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="45" height="20" fill="#000" />
|
||||
<rect x="45" width="45" height="20" fill="#e2ffe1" />
|
||||
<rect width="90" height="20" fill="url(#s)" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="235"
|
||||
y="150"
|
||||
fill="#010101"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
cactus
|
||||
</text>
|
||||
<text x="235" y="140" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="665"
|
||||
y="150"
|
||||
fill="#ccc"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
grown
|
||||
</text>
|
||||
<text x="665" y="140" transform="scale(.1)" fill="#333" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "flat-square" template badge generation should match snapshots: message/label, no logo 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -609,6 +729,70 @@ exports['The badge generator "flat-square" template badge generation should matc
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "flat-square" template badge generation should match snapshots: black text when the label color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="20"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<g shape-rendering="crispEdges">
|
||||
<rect width="45" height="20" fill="#f3f3f3" />
|
||||
<rect x="45" width="45" height="20" fill="#000" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text x="235" y="140" transform="scale(.1)" fill="#333" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text x="665" y="140" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "flat-square" template badge generation should match snapshots: black text when the message color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="20"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<g shape-rendering="crispEdges">
|
||||
<rect width="45" height="20" fill="#000" />
|
||||
<rect x="45" width="45" height="20" fill="#e2ffe1" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text x="235" y="140" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text x="665" y="140" transform="scale(.1)" fill="#333" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "plastic" template badge generation should match snapshots: message/label, no logo 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -965,6 +1149,130 @@ exports['The badge generator "plastic" template badge generation should match sn
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "plastic" template badge generation should match snapshots: black text when the label color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="18"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".7" />
|
||||
<stop offset=".1" stop-color="#aaa" stop-opacity=".1" />
|
||||
<stop offset=".9" stop-color="#000" stop-opacity=".3" />
|
||||
<stop offset="1" stop-color="#000" stop-opacity=".5" />
|
||||
</linearGradient>
|
||||
<clipPath id="r"><rect width="90" height="18" rx="4" fill="#fff" /></clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="45" height="18" fill="#f3f3f3" />
|
||||
<rect x="45" width="45" height="18" fill="#000" />
|
||||
<rect width="90" height="18" fill="url(#s)" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="235"
|
||||
y="140"
|
||||
fill="#ccc"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
cactus
|
||||
</text>
|
||||
<text x="235" y="130" transform="scale(.1)" fill="#333" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="665"
|
||||
y="140"
|
||||
fill="#010101"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
grown
|
||||
</text>
|
||||
<text x="665" y="130" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "plastic" template badge generation should match snapshots: black text when the message color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="18"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".7" />
|
||||
<stop offset=".1" stop-color="#aaa" stop-opacity=".1" />
|
||||
<stop offset=".9" stop-color="#000" stop-opacity=".3" />
|
||||
<stop offset="1" stop-color="#000" stop-opacity=".5" />
|
||||
</linearGradient>
|
||||
<clipPath id="r"><rect width="90" height="18" rx="4" fill="#fff" /></clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="45" height="18" fill="#000" />
|
||||
<rect x="45" width="45" height="18" fill="#e2ffe1" />
|
||||
<rect width="90" height="18" fill="url(#s)" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="235"
|
||||
y="140"
|
||||
fill="#010101"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
cactus
|
||||
</text>
|
||||
<text x="235" y="130" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="665"
|
||||
y="140"
|
||||
fill="#ccc"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
grown
|
||||
</text>
|
||||
<text x="665" y="130" transform="scale(.1)" fill="#333" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "for-the-badge" template badge generation should match snapshots: message/label, no logo 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -1218,6 +1526,84 @@ exports['The badge generator "for-the-badge" template badge generation should ma
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "for-the-badge" template badge generation should match snapshots: black text when the label color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="146.75"
|
||||
height="28"
|
||||
role="img"
|
||||
aria-label="CACTUS: GROWN"
|
||||
>
|
||||
<title>CACTUS: GROWN</title>
|
||||
<g shape-rendering="crispEdges">
|
||||
<rect width="72.5" height="28" fill="#f3f3f3" />
|
||||
<rect x="72.5" width="74.25" height="28" fill="#000" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="100"
|
||||
>
|
||||
<text transform="scale(.1)" x="362.5" y="175" textLength="485" fill="#333">
|
||||
CACTUS
|
||||
</text>
|
||||
<text
|
||||
transform="scale(.1)"
|
||||
x="1096.25"
|
||||
y="175"
|
||||
textLength="502.5"
|
||||
fill="#fff"
|
||||
font-weight="bold"
|
||||
>
|
||||
GROWN
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "for-the-badge" template badge generation should match snapshots: black text when the message color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="146.75"
|
||||
height="28"
|
||||
role="img"
|
||||
aria-label="CACTUS: GROWN"
|
||||
>
|
||||
<title>CACTUS: GROWN</title>
|
||||
<g shape-rendering="crispEdges">
|
||||
<rect width="72.5" height="28" fill="#000" />
|
||||
<rect x="72.5" width="74.25" height="28" fill="#e2ffe1" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="100"
|
||||
>
|
||||
<text transform="scale(.1)" x="362.5" y="175" textLength="485" fill="#fff">
|
||||
CACTUS
|
||||
</text>
|
||||
<text
|
||||
transform="scale(.1)"
|
||||
x="1096.25"
|
||||
y="175"
|
||||
textLength="502.5"
|
||||
fill="#333"
|
||||
font-weight="bold"
|
||||
>
|
||||
GROWN
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator "social" template badge generation should match snapshots: message/label, no logo 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -1843,102 +2229,3 @@ exports['The badge generator badges with logos should always produce the same ba
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator text colors should use black text when the label color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="90"
|
||||
height="20"
|
||||
role="img"
|
||||
aria-label="cactus: grown"
|
||||
>
|
||||
<title>cactus: grown</title>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
|
||||
<stop offset="1" stop-opacity=".1" />
|
||||
</linearGradient>
|
||||
<clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff" /></clipPath>
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="45" height="20" fill="#f3f3f3" />
|
||||
<rect x="45" width="45" height="20" fill="#000" />
|
||||
<rect width="90" height="20" fill="url(#s)" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="110"
|
||||
>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="235"
|
||||
y="150"
|
||||
fill="#ccc"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
cactus
|
||||
</text>
|
||||
<text x="235" y="140" transform="scale(.1)" fill="#333" textLength="350">
|
||||
cactus
|
||||
</text>
|
||||
<text
|
||||
aria-hidden="true"
|
||||
x="665"
|
||||
y="150"
|
||||
fill="#010101"
|
||||
fill-opacity=".3"
|
||||
transform="scale(.1)"
|
||||
textLength="350"
|
||||
>
|
||||
grown
|
||||
</text>
|
||||
<text x="665" y="140" transform="scale(.1)" fill="#fff" textLength="350">
|
||||
grown
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
exports['The badge generator text colors should use black text when the message color is light 1'] = `
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="146.75"
|
||||
height="28"
|
||||
role="img"
|
||||
aria-label="CACTUS: GROWN"
|
||||
>
|
||||
<title>CACTUS: GROWN</title>
|
||||
<g shape-rendering="crispEdges">
|
||||
<rect width="72.5" height="28" fill="#000" />
|
||||
<rect x="72.5" width="74.25" height="28" fill="#e2ffe1" />
|
||||
</g>
|
||||
<g
|
||||
fill="#fff"
|
||||
text-anchor="middle"
|
||||
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
|
||||
text-rendering="geometricPrecision"
|
||||
font-size="100"
|
||||
>
|
||||
<text transform="scale(.1)" x="362.5" y="175" textLength="485" fill="#fff">
|
||||
CACTUS
|
||||
</text>
|
||||
<text
|
||||
transform="scale(.1)"
|
||||
x="1096.25"
|
||||
y="175"
|
||||
textLength="502.5"
|
||||
fill="#333"
|
||||
font-weight="bold"
|
||||
>
|
||||
GROWN
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
`
|
||||
|
||||
6
app.json
6
app.json
@@ -4,7 +4,7 @@
|
||||
"keywords": ["badge", "github", "svg", "status"],
|
||||
"website": "https://shields.io/",
|
||||
"repository": "https://github.com/badges/shields",
|
||||
"logo": "http://shields.io/favicon.png",
|
||||
"logo": "https://shields.io/favicon.png",
|
||||
"env": {
|
||||
"CYPRESS_INSTALL_BINARY": {
|
||||
"description": "Disable the cypress binary installation",
|
||||
@@ -31,6 +31,10 @@
|
||||
"TWITCH_CLIENT_SECRET": {
|
||||
"description": "Configure the client secret to be used for the Twitch service.",
|
||||
"required": false
|
||||
},
|
||||
"WEBLATE_API_KEY": {
|
||||
"description": "Configure the API key to be used for the Weblate service.",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"formation": {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 4.0.0 [WIP]
|
||||
|
||||
- Drop compatibility with Node 10
|
||||
|
||||
## 3.3.1
|
||||
|
||||
- Improve font measuring in for-the-badge and social styles
|
||||
- Make for-the-badge letter spacing more predictable
|
||||
|
||||
## 3.3.0
|
||||
|
||||
- Readability improvements: a dark font color is automatically used when the badge's background is too light. For example: 
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
|
||||
const anafanafo = require('anafanafo')
|
||||
const { brightness } = require('./color')
|
||||
const { XmlElement, escapeXml } = require('./xml')
|
||||
const { XmlElement, ElementList } = require('./xml')
|
||||
|
||||
// https://github.com/badges/shields/pull/1132
|
||||
const FONT_SCALE_UP_FACTOR = 10
|
||||
const FONT_SCALE_DOWN_VALUE = 'scale(.1)'
|
||||
|
||||
const FONT_FAMILY = 'Verdana,Geneva,DejaVu Sans,sans-serif'
|
||||
const fontFamily = `font-family="${FONT_FAMILY}"`
|
||||
const socialFontFamily =
|
||||
'font-family="Helvetica Neue,Helvetica,Arial,sans-serif"'
|
||||
const brightnessThreshold = 0.69
|
||||
const WIDTH_FONT = '11px Verdana'
|
||||
const SOCIAL_FONT_FAMILY = 'Helvetica Neue,Helvetica,Arial,sans-serif'
|
||||
|
||||
function capitalize(s) {
|
||||
return `${s.charAt(0).toUpperCase()}${s.slice(1)}`
|
||||
}
|
||||
|
||||
function colorsForBackground(color) {
|
||||
const brightnessThreshold = 0.69
|
||||
if (brightness(color) <= brightnessThreshold) {
|
||||
return { textColor: '#fff', shadowColor: '#010101' }
|
||||
} else {
|
||||
@@ -53,127 +52,61 @@ function shouldWrapBodyWithLink({ links }) {
|
||||
return hasLeftLink && !hasRightLink
|
||||
}
|
||||
|
||||
function renderAriaAttributes({ accessibleText, links }) {
|
||||
const { hasLink } = hasLinks({ links })
|
||||
return hasLink ? '' : `role="img" aria-label="${escapeXml(accessibleText)}"`
|
||||
}
|
||||
|
||||
function renderTitle({ accessibleText, links }) {
|
||||
const { hasLink } = hasLinks({ links })
|
||||
return hasLink ? '' : `<title>${escapeXml(accessibleText)}</title>`
|
||||
}
|
||||
|
||||
function renderLogo({
|
||||
logo,
|
||||
badgeHeight,
|
||||
horizPadding,
|
||||
logoWidth = 14,
|
||||
logoPadding = 0,
|
||||
}) {
|
||||
if (logo) {
|
||||
const logoHeight = 14
|
||||
const y = (badgeHeight - logoHeight) / 2
|
||||
const x = horizPadding
|
||||
return {
|
||||
hasLogo: true,
|
||||
totalLogoWidth: logoWidth + logoPadding,
|
||||
renderedLogo: `<image x="${x}" y="${y}" width="${logoWidth}" height="${logoHeight}" xlink:href="${escapeXml(
|
||||
logo
|
||||
)}"/>`,
|
||||
}
|
||||
} else {
|
||||
return { hasLogo: false, totalLogoWidth: 0, renderedLogo: '' }
|
||||
}
|
||||
}
|
||||
|
||||
function renderLink({
|
||||
link,
|
||||
height,
|
||||
textLength,
|
||||
horizPadding,
|
||||
leftMargin,
|
||||
renderedText,
|
||||
}) {
|
||||
const rectHeight = height
|
||||
const rectWidth = textLength + horizPadding * 2
|
||||
const rectX = leftMargin > 1 ? leftMargin + 1 : 0
|
||||
return `<a target="_blank" xlink:href="${escapeXml(link)}">
|
||||
<rect width="${rectWidth}" x="${rectX}" height="${rectHeight}" fill="rgba(0,0,0,0)" />
|
||||
${renderedText}
|
||||
</a>`
|
||||
}
|
||||
|
||||
function renderText({
|
||||
leftMargin,
|
||||
horizPadding = 0,
|
||||
content,
|
||||
link,
|
||||
height,
|
||||
verticalMargin = 0,
|
||||
shadow = false,
|
||||
color,
|
||||
}) {
|
||||
if (!content.length) {
|
||||
return { renderedText: '', width: 0 }
|
||||
}
|
||||
|
||||
const textLength = preferredWidthOf(content, { font: '11px Verdana' })
|
||||
const escapedContent = escapeXml(content)
|
||||
|
||||
const shadowMargin = 150 + verticalMargin
|
||||
const textMargin = 140 + verticalMargin
|
||||
|
||||
const outTextLength = 10 * textLength
|
||||
const x = 10 * (leftMargin + 0.5 * textLength + horizPadding)
|
||||
|
||||
let renderedText = ''
|
||||
const { textColor, shadowColor } = colorsForBackground(color)
|
||||
if (shadow) {
|
||||
renderedText = `<text aria-hidden="true" x="${x}" y="${shadowMargin}" fill="${shadowColor}" fill-opacity=".3" transform="scale(.1)" textLength="${outTextLength}">${escapedContent}</text>`
|
||||
}
|
||||
renderedText += `<text x="${x}" y="${textMargin}" transform="scale(.1)" fill="${textColor}" textLength="${outTextLength}">${escapedContent}</text>`
|
||||
|
||||
return {
|
||||
renderedText: link
|
||||
? renderLink({
|
||||
link,
|
||||
height,
|
||||
textLength,
|
||||
horizPadding,
|
||||
leftMargin,
|
||||
renderedText,
|
||||
})
|
||||
: renderedText,
|
||||
width: textLength,
|
||||
}
|
||||
function getLogoElement({ logo, horizPadding, badgeHeight, logoWidth }) {
|
||||
const logoHeight = 14
|
||||
if (!logo) return ''
|
||||
return new XmlElement({
|
||||
name: 'image',
|
||||
attrs: {
|
||||
x: horizPadding,
|
||||
y: 0.5 * (badgeHeight - logoHeight),
|
||||
width: logoWidth,
|
||||
height: logoHeight,
|
||||
'xlink:href': logo,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function renderBadge(
|
||||
{ links, leftWidth, rightWidth, height, accessibleText },
|
||||
main
|
||||
content
|
||||
) {
|
||||
const width = leftWidth + rightWidth
|
||||
const leftLink = escapeXml(links[0])
|
||||
const leftLink = links[0]
|
||||
const { hasLink } = hasLinks({ links })
|
||||
|
||||
return `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}" ${renderAriaAttributes(
|
||||
{ links, accessibleText }
|
||||
)}>
|
||||
const title = hasLink
|
||||
? ''
|
||||
: new XmlElement({ name: 'title', content: [accessibleText] })
|
||||
|
||||
${renderTitle({ accessibleText, links })}
|
||||
${
|
||||
shouldWrapBodyWithLink({ links })
|
||||
? `<a target="_blank" xlink:href="${leftLink}">${main}</a>`
|
||||
: main
|
||||
}
|
||||
</svg>`
|
||||
const body = shouldWrapBodyWithLink({ links })
|
||||
? new XmlElement({
|
||||
name: 'a',
|
||||
content,
|
||||
attrs: { target: '_blank', 'xlink:href': leftLink },
|
||||
})
|
||||
: new ElementList({ content })
|
||||
|
||||
const svgAttrs = {
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||
width,
|
||||
height,
|
||||
}
|
||||
if (!hasLink) {
|
||||
svgAttrs.role = 'img'
|
||||
svgAttrs['aria-label'] = accessibleText
|
||||
}
|
||||
|
||||
const svg = new XmlElement({
|
||||
name: 'svg',
|
||||
content: [title, body],
|
||||
attrs: svgAttrs,
|
||||
})
|
||||
return svg.render()
|
||||
}
|
||||
|
||||
class Badge {
|
||||
static get fontFamily() {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
static get height() {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
@@ -197,41 +130,25 @@ class Badge {
|
||||
labelColor,
|
||||
}) {
|
||||
const horizPadding = 5
|
||||
const { hasLogo, totalLogoWidth, renderedLogo } = renderLogo({
|
||||
logo,
|
||||
badgeHeight: this.constructor.height,
|
||||
horizPadding,
|
||||
logoWidth,
|
||||
logoPadding,
|
||||
})
|
||||
const hasLogo = !!logo
|
||||
const totalLogoWidth = logoWidth + logoPadding
|
||||
const accessibleText = createAccessibleText({ label, message })
|
||||
|
||||
const hasLabel = label.length || labelColor
|
||||
if (labelColor == null) {
|
||||
labelColor = '#555'
|
||||
}
|
||||
|
||||
const [leftLink, rightLink] = links
|
||||
|
||||
labelColor = hasLabel || hasLogo ? labelColor : color
|
||||
labelColor = escapeXml(labelColor)
|
||||
color = escapeXml(color)
|
||||
|
||||
const labelMargin = totalLogoWidth + 1
|
||||
|
||||
const { renderedText: renderedLabel, width: labelWidth } = renderText({
|
||||
leftMargin: labelMargin,
|
||||
horizPadding,
|
||||
content: label,
|
||||
link: !shouldWrapBodyWithLink({ links }) && leftLink,
|
||||
height: this.constructor.height,
|
||||
verticalMargin: this.constructor.verticalMargin,
|
||||
shadow: this.constructor.shadow,
|
||||
color: labelColor,
|
||||
})
|
||||
|
||||
const labelWidth = label.length
|
||||
? preferredWidthOf(label, { font: WIDTH_FONT })
|
||||
: 0
|
||||
const leftWidth = hasLabel
|
||||
? labelWidth + 2 * horizPadding + totalLogoWidth
|
||||
: 0
|
||||
|
||||
const messageWidth = preferredWidthOf(message, { font: WIDTH_FONT })
|
||||
let messageMargin = leftWidth - (message.length ? 1 : 0)
|
||||
if (!hasLabel) {
|
||||
if (hasLogo) {
|
||||
@@ -240,18 +157,6 @@ class Badge {
|
||||
messageMargin = messageMargin + 1
|
||||
}
|
||||
}
|
||||
|
||||
const { renderedText: renderedMessage, width: messageWidth } = renderText({
|
||||
leftMargin: messageMargin,
|
||||
horizPadding,
|
||||
content: message,
|
||||
link: rightLink,
|
||||
height: this.constructor.height,
|
||||
verticalMargin: this.constructor.verticalMargin,
|
||||
shadow: this.constructor.shadow,
|
||||
color,
|
||||
})
|
||||
|
||||
let rightWidth = messageWidth + 2 * horizPadding
|
||||
if (hasLogo && !hasLabel) {
|
||||
rightWidth += totalLogoWidth + horizPadding - 1
|
||||
@@ -259,9 +164,12 @@ class Badge {
|
||||
|
||||
const width = leftWidth + rightWidth
|
||||
|
||||
const accessibleText = createAccessibleText({ label, message })
|
||||
|
||||
this.horizPadding = horizPadding
|
||||
this.labelMargin = labelMargin
|
||||
this.messageMargin = messageMargin
|
||||
this.links = links
|
||||
this.labelWidth = labelWidth
|
||||
this.messageWidth = messageWidth
|
||||
this.leftWidth = leftWidth
|
||||
this.rightWidth = rightWidth
|
||||
this.width = width
|
||||
@@ -270,25 +178,174 @@ class Badge {
|
||||
this.label = label
|
||||
this.message = message
|
||||
this.accessibleText = accessibleText
|
||||
this.renderedLogo = renderedLogo
|
||||
this.renderedLabel = renderedLabel
|
||||
this.renderedMessage = renderedMessage
|
||||
|
||||
this.logoElement = getLogoElement({
|
||||
logo,
|
||||
horizPadding,
|
||||
badgeHeight: this.constructor.height,
|
||||
logoWidth,
|
||||
})
|
||||
this.foregroundGroupElement = this.getForegroundGroupElement()
|
||||
}
|
||||
|
||||
static render(params) {
|
||||
return new this(params).render()
|
||||
}
|
||||
|
||||
getTextElement({ leftMargin, content, link, color, textWidth, linkWidth }) {
|
||||
if (!content.length) return ''
|
||||
|
||||
const { textColor, shadowColor } = colorsForBackground(color)
|
||||
const x =
|
||||
FONT_SCALE_UP_FACTOR * (leftMargin + 0.5 * textWidth + this.horizPadding)
|
||||
|
||||
const text = new XmlElement({
|
||||
name: 'text',
|
||||
content: [content],
|
||||
attrs: {
|
||||
x,
|
||||
y: 140 + this.constructor.verticalMargin,
|
||||
transform: FONT_SCALE_DOWN_VALUE,
|
||||
fill: textColor,
|
||||
textLength: FONT_SCALE_UP_FACTOR * textWidth,
|
||||
},
|
||||
})
|
||||
|
||||
const shadowText = new XmlElement({
|
||||
name: 'text',
|
||||
content: [content],
|
||||
attrs: {
|
||||
'aria-hidden': 'true',
|
||||
x,
|
||||
y: 150 + this.constructor.verticalMargin,
|
||||
fill: shadowColor,
|
||||
'fill-opacity': '.3',
|
||||
transform: FONT_SCALE_DOWN_VALUE,
|
||||
textLength: FONT_SCALE_UP_FACTOR * textWidth,
|
||||
},
|
||||
})
|
||||
const shadow = this.constructor.shadow ? shadowText : ''
|
||||
|
||||
if (!link) {
|
||||
return new ElementList({ content: [shadow, text] })
|
||||
}
|
||||
|
||||
const rect = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
width: linkWidth,
|
||||
x: leftMargin > 1 ? leftMargin + 1 : 0,
|
||||
height: this.constructor.height,
|
||||
fill: 'rgba(0,0,0,0)',
|
||||
},
|
||||
})
|
||||
return new XmlElement({
|
||||
name: 'a',
|
||||
content: [rect, shadow, text],
|
||||
attrs: { target: '_blank', 'xlink:href': link },
|
||||
})
|
||||
}
|
||||
|
||||
getLabelElement() {
|
||||
const leftLink = this.links[0]
|
||||
return this.getTextElement({
|
||||
leftMargin: this.labelMargin,
|
||||
content: this.label,
|
||||
link: !shouldWrapBodyWithLink({ links: this.links })
|
||||
? leftLink
|
||||
: undefined,
|
||||
color: this.labelColor,
|
||||
textWidth: this.labelWidth,
|
||||
linkWidth: this.leftWidth,
|
||||
})
|
||||
}
|
||||
|
||||
getMessageElement() {
|
||||
const rightLink = this.links[1]
|
||||
return this.getTextElement({
|
||||
leftMargin: this.messageMargin,
|
||||
content: this.message,
|
||||
link: rightLink,
|
||||
color: this.color,
|
||||
textWidth: this.messageWidth,
|
||||
linkWidth: this.rightWidth,
|
||||
})
|
||||
}
|
||||
|
||||
getClipPathElement(rx) {
|
||||
return new XmlElement({
|
||||
name: 'clipPath',
|
||||
content: [
|
||||
new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
width: this.width,
|
||||
height: this.constructor.height,
|
||||
rx,
|
||||
fill: '#fff',
|
||||
},
|
||||
}),
|
||||
],
|
||||
attrs: { id: 'r' },
|
||||
})
|
||||
}
|
||||
|
||||
getBackgroundGroupElement({ withGradient, attrs }) {
|
||||
const leftRect = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
width: this.leftWidth,
|
||||
height: this.constructor.height,
|
||||
fill: this.labelColor,
|
||||
},
|
||||
})
|
||||
const rightRect = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
x: this.leftWidth,
|
||||
width: this.rightWidth,
|
||||
height: this.constructor.height,
|
||||
fill: this.color,
|
||||
},
|
||||
})
|
||||
const gradient = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
width: this.width,
|
||||
height: this.constructor.height,
|
||||
fill: 'url(#s)',
|
||||
},
|
||||
})
|
||||
const content = withGradient
|
||||
? [leftRect, rightRect, gradient]
|
||||
: [leftRect, rightRect]
|
||||
return new XmlElement({ name: 'g', content, attrs })
|
||||
}
|
||||
|
||||
getForegroundGroupElement() {
|
||||
return new XmlElement({
|
||||
name: 'g',
|
||||
content: [
|
||||
this.logoElement,
|
||||
this.getLabelElement(),
|
||||
this.getMessageElement(),
|
||||
],
|
||||
attrs: {
|
||||
fill: '#fff',
|
||||
'text-anchor': 'middle',
|
||||
'font-family': FONT_FAMILY,
|
||||
'text-rendering': 'geometricPrecision',
|
||||
'font-size': 110,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
}
|
||||
|
||||
class Plastic extends Badge {
|
||||
static get fontFamily() {
|
||||
return fontFamily
|
||||
}
|
||||
|
||||
static get height() {
|
||||
return 18
|
||||
}
|
||||
@@ -302,6 +359,36 @@ class Plastic extends Badge {
|
||||
}
|
||||
|
||||
render() {
|
||||
const gradient = new XmlElement({
|
||||
name: 'linearGradient',
|
||||
content: [
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 0, 'stop-color': '#fff', 'stop-opacity': '.7' },
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: '.1', 'stop-color': '#aaa', 'stop-opacity': '.1' },
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: '.9', 'stop-color': '#000', 'stop-opacity': '.3' },
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 1, 'stop-color': '#000', 'stop-opacity': '.5' },
|
||||
}),
|
||||
],
|
||||
attrs: { id: 's', x2: 0, y2: '100%' },
|
||||
})
|
||||
|
||||
const clipPath = this.getClipPathElement(4)
|
||||
|
||||
const backgroundGroup = this.getBackgroundGroupElement({
|
||||
withGradient: true,
|
||||
attrs: { 'clip-path': 'url(#r)' },
|
||||
})
|
||||
|
||||
return renderBadge(
|
||||
{
|
||||
links: this.links,
|
||||
@@ -310,38 +397,12 @@ class Plastic extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
`
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".7"/>
|
||||
<stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
|
||||
<stop offset=".9" stop-color="#000" stop-opacity=".3"/>
|
||||
<stop offset="1" stop-color="#000" stop-opacity=".5"/>
|
||||
</linearGradient>
|
||||
|
||||
<clipPath id="r">
|
||||
<rect width="${this.width}" height="${this.constructor.height}" rx="4" fill="#fff"/>
|
||||
</clipPath>
|
||||
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
|
||||
<rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
|
||||
<rect width="${this.width}" height="${this.constructor.height}" fill="url(#s)"/>
|
||||
</g>
|
||||
|
||||
<g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} text-rendering="geometricPrecision" font-size="110">
|
||||
${this.renderedLogo}
|
||||
${this.renderedLabel}
|
||||
${this.renderedMessage}
|
||||
</g>`
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Flat extends Badge {
|
||||
static get fontFamily() {
|
||||
return fontFamily
|
||||
}
|
||||
|
||||
static get height() {
|
||||
return 20
|
||||
}
|
||||
@@ -355,6 +416,28 @@ class Flat extends Badge {
|
||||
}
|
||||
|
||||
render() {
|
||||
const gradient = new XmlElement({
|
||||
name: 'linearGradient',
|
||||
content: [
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 0, 'stop-color': '#bbb', 'stop-opacity': '.1' },
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 1, 'stop-opacity': '.1' },
|
||||
}),
|
||||
],
|
||||
attrs: { id: 's', x2: 0, y2: '100%' },
|
||||
})
|
||||
|
||||
const clipPath = this.getClipPathElement(3)
|
||||
|
||||
const backgroundGroup = this.getBackgroundGroupElement({
|
||||
withGradient: true,
|
||||
attrs: { 'clip-path': 'url(#r)' },
|
||||
})
|
||||
|
||||
return renderBadge(
|
||||
{
|
||||
links: this.links,
|
||||
@@ -363,36 +446,12 @@ class Flat extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
`
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
|
||||
<clipPath id="r">
|
||||
<rect width="${this.width}" height="${this.constructor.height}" rx="3" fill="#fff"/>
|
||||
</clipPath>
|
||||
|
||||
<g clip-path="url(#r)">
|
||||
<rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
|
||||
<rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
|
||||
<rect width="${this.width}" height="${this.constructor.height}" fill="url(#s)"/>
|
||||
</g>
|
||||
|
||||
<g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} text-rendering="geometricPrecision" font-size="110">
|
||||
${this.renderedLogo}
|
||||
${this.renderedLabel}
|
||||
${this.renderedMessage}
|
||||
</g>`
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FlatSquare extends Badge {
|
||||
static get fontFamily() {
|
||||
return fontFamily
|
||||
}
|
||||
|
||||
static get height() {
|
||||
return 20
|
||||
}
|
||||
@@ -406,6 +465,11 @@ class FlatSquare extends Badge {
|
||||
}
|
||||
|
||||
render() {
|
||||
const backgroundGroup = this.getBackgroundGroupElement({
|
||||
withGradient: false,
|
||||
attrs: { 'shape-rendering': 'crispEdges' },
|
||||
})
|
||||
|
||||
return renderBadge(
|
||||
{
|
||||
links: this.links,
|
||||
@@ -414,17 +478,7 @@ class FlatSquare extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
`
|
||||
<g shape-rendering="crispEdges">
|
||||
<rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
|
||||
<rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
|
||||
</g>
|
||||
|
||||
<g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} text-rendering="geometricPrecision" font-size="110">
|
||||
${this.renderedLogo}
|
||||
${this.renderedLabel}
|
||||
${this.renderedMessage}
|
||||
</g>`
|
||||
[backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -448,13 +502,7 @@ function social({
|
||||
const labelHorizPadding = 5
|
||||
const messageHorizPadding = 4
|
||||
const horizGutter = 6
|
||||
const { totalLogoWidth, renderedLogo } = renderLogo({
|
||||
logo,
|
||||
badgeHeight: externalHeight,
|
||||
horizPadding: labelHorizPadding,
|
||||
logoWidth,
|
||||
logoPadding,
|
||||
})
|
||||
const totalLogoWidth = logoWidth + logoPadding
|
||||
const hasMessage = message.length
|
||||
|
||||
const font = 'bold 11px Helvetica'
|
||||
@@ -463,75 +511,235 @@ function social({
|
||||
const labelRectWidth = labelTextWidth + totalLogoWidth + 2 * labelHorizPadding
|
||||
const messageRectWidth = messageTextWidth + 2 * messageHorizPadding
|
||||
|
||||
let [leftLink, rightLink] = links
|
||||
leftLink = escapeXml(leftLink)
|
||||
rightLink = escapeXml(rightLink)
|
||||
const [leftLink, rightLink] = links
|
||||
const { hasLeftLink, hasRightLink, hasLink } = hasLinks({ links })
|
||||
|
||||
const accessibleText = createAccessibleText({ label, message })
|
||||
|
||||
function renderMessageBubble() {
|
||||
function getMessageBubble() {
|
||||
if (!hasMessage) return ''
|
||||
|
||||
const messageBubbleMainX = labelRectWidth + horizGutter + 0.5
|
||||
const messageBubbleNotchX = labelRectWidth + horizGutter
|
||||
return `
|
||||
<rect x="${messageBubbleMainX}" y="0.5" width="${messageRectWidth}" height="${internalHeight}" rx="2" fill="#fafafa"/>
|
||||
<rect x="${messageBubbleNotchX}" y="7.5" width="0.5" height="5" stroke="#fafafa"/>
|
||||
<path d="M${messageBubbleMainX} 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/>
|
||||
`
|
||||
const content = [
|
||||
new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
x: messageBubbleMainX,
|
||||
y: 0.5,
|
||||
width: messageRectWidth,
|
||||
height: internalHeight,
|
||||
rx: 2,
|
||||
fill: '#fafafa',
|
||||
},
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
x: messageBubbleNotchX,
|
||||
y: 7.5,
|
||||
width: 0.5,
|
||||
height: 5,
|
||||
stroke: '#fafafa',
|
||||
},
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'path',
|
||||
attrs: {
|
||||
d: `M${messageBubbleMainX} 6.5 l-3 3v1 l3 3`,
|
||||
stroke: 'd5d5d5',
|
||||
fill: '#fafafa',
|
||||
},
|
||||
}),
|
||||
]
|
||||
return new ElementList({ content })
|
||||
}
|
||||
|
||||
function renderLabelText() {
|
||||
function getLabelText() {
|
||||
const labelTextX =
|
||||
10 * (totalLogoWidth + labelTextWidth / 2 + labelHorizPadding)
|
||||
const labelTextLength = 10 * labelTextWidth
|
||||
const escapedLabel = escapeXml(label)
|
||||
FONT_SCALE_UP_FACTOR *
|
||||
(totalLogoWidth + labelTextWidth / 2 + labelHorizPadding)
|
||||
const labelTextLength = FONT_SCALE_UP_FACTOR * labelTextWidth
|
||||
const shouldWrapWithLink = hasLeftLink && !shouldWrapBodyWithLink({ links })
|
||||
|
||||
const rect = `<rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="${labelRectWidth}" height="${internalHeight}" rx="2" />`
|
||||
const shadow = `<text aria-hidden="true" x="${labelTextX}" y="150" fill="#fff" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>`
|
||||
const text = `<text x="${labelTextX}" y="140" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>`
|
||||
const rect = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
id: 'llink',
|
||||
stroke: '#d5d5d5',
|
||||
fill: 'url(#a)',
|
||||
x: '.5',
|
||||
y: '.5',
|
||||
width: labelRectWidth,
|
||||
height: internalHeight,
|
||||
rx: 2,
|
||||
},
|
||||
})
|
||||
const shadow = new XmlElement({
|
||||
name: 'text',
|
||||
content: [label],
|
||||
attrs: {
|
||||
'aria-hidden': 'true',
|
||||
x: labelTextX,
|
||||
y: 150,
|
||||
fill: '#fff',
|
||||
transform: FONT_SCALE_DOWN_VALUE,
|
||||
textLength: labelTextLength,
|
||||
},
|
||||
})
|
||||
const text = new XmlElement({
|
||||
name: 'text',
|
||||
content: [label],
|
||||
attrs: {
|
||||
x: labelTextX,
|
||||
y: 140,
|
||||
transform: FONT_SCALE_DOWN_VALUE,
|
||||
textLength: labelTextLength,
|
||||
},
|
||||
})
|
||||
|
||||
return shouldWrapWithLink
|
||||
? `
|
||||
<a target="_blank" xlink:href="${leftLink}">
|
||||
${shadow}
|
||||
${text}
|
||||
${rect}
|
||||
</a>
|
||||
`
|
||||
: `
|
||||
${rect}
|
||||
${shadow}
|
||||
${text}
|
||||
`
|
||||
? new XmlElement({
|
||||
name: 'a',
|
||||
content: [shadow, text, rect],
|
||||
attrs: { target: '_blank', 'xlink:href': leftLink },
|
||||
})
|
||||
: new ElementList({ content: [rect, shadow, text] })
|
||||
}
|
||||
|
||||
function renderMessageText() {
|
||||
const messageTextX =
|
||||
10 * (labelRectWidth + horizGutter + messageRectWidth / 2)
|
||||
const messageTextLength = 10 * messageTextWidth
|
||||
const escapedMessage = escapeXml(message)
|
||||
function getMessageText() {
|
||||
if (!hasMessage) return ''
|
||||
|
||||
const rect = `<rect width="${messageRectWidth + 1}" x="${
|
||||
labelRectWidth + horizGutter
|
||||
}" height="${internalHeight + 1}" fill="rgba(0,0,0,0)" />`
|
||||
const shadow = `<text aria-hidden="true" x="${messageTextX}" y="150" fill="#fff" transform="scale(.1)" textLength="${messageTextLength}">${escapedMessage}</text>`
|
||||
const text = `<text id="rlink" x="${messageTextX}" y="140" transform="scale(.1)" textLength="${messageTextLength}">${escapedMessage}</text>`
|
||||
const messageTextX =
|
||||
FONT_SCALE_UP_FACTOR *
|
||||
(labelRectWidth + horizGutter + messageRectWidth / 2)
|
||||
const messageTextLength = FONT_SCALE_UP_FACTOR * messageTextWidth
|
||||
|
||||
const rect = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
width: messageRectWidth + 1,
|
||||
x: labelRectWidth + horizGutter,
|
||||
height: internalHeight + 1,
|
||||
fill: 'rgba(0,0,0,0)',
|
||||
},
|
||||
})
|
||||
const shadow = new XmlElement({
|
||||
name: 'text',
|
||||
content: [message],
|
||||
attrs: {
|
||||
'aria-hidden': 'true',
|
||||
x: messageTextX,
|
||||
y: 150,
|
||||
fill: '#fff',
|
||||
transform: FONT_SCALE_DOWN_VALUE,
|
||||
textLength: messageTextLength,
|
||||
},
|
||||
})
|
||||
const text = new XmlElement({
|
||||
name: 'text',
|
||||
content: [message],
|
||||
attrs: {
|
||||
id: 'rlink',
|
||||
x: messageTextX,
|
||||
y: 140,
|
||||
transform: FONT_SCALE_DOWN_VALUE,
|
||||
textLength: messageTextLength,
|
||||
},
|
||||
})
|
||||
|
||||
return hasRightLink
|
||||
? `
|
||||
<a target="_blank" xlink:href="${rightLink}">
|
||||
${rect}
|
||||
${shadow}
|
||||
${text}
|
||||
</a>
|
||||
`
|
||||
: `
|
||||
${shadow}
|
||||
${text}
|
||||
`
|
||||
? new XmlElement({
|
||||
name: 'a',
|
||||
content: [rect, shadow, text],
|
||||
attrs: { target: '_blank', 'xlink:href': rightLink },
|
||||
})
|
||||
: new ElementList({ content: [shadow, text] })
|
||||
}
|
||||
|
||||
const style = new XmlElement({
|
||||
name: 'style',
|
||||
content: [
|
||||
'a:hover #llink{fill:url(#b);stroke:#ccc}a:hover #rlink{fill:#4183c4}',
|
||||
],
|
||||
})
|
||||
const gradients = new ElementList({
|
||||
content: [
|
||||
new XmlElement({
|
||||
name: 'linearGradient',
|
||||
content: [
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: {
|
||||
offset: 0,
|
||||
'stop-color': '#fcfcfc',
|
||||
'stop-opacity': 0,
|
||||
},
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 1, 'stop-opacity': '.1' },
|
||||
}),
|
||||
],
|
||||
attrs: { id: 'a', x2: 0, y2: '100%' },
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'linearGradient',
|
||||
content: [
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 0, 'stop-color': '#ccc', 'stop-opacity': '.1' },
|
||||
}),
|
||||
new XmlElement({
|
||||
name: 'stop',
|
||||
attrs: { offset: 1, 'stop-opacity': '.1' },
|
||||
}),
|
||||
],
|
||||
attrs: { id: 'b', x2: 0, y2: '100%' },
|
||||
}),
|
||||
],
|
||||
})
|
||||
const labelRect = new XmlElement({
|
||||
name: 'rect',
|
||||
attrs: {
|
||||
stroke: 'none',
|
||||
fill: '#fcfcfc',
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
width: labelRectWidth,
|
||||
height: internalHeight,
|
||||
rx: 2,
|
||||
},
|
||||
})
|
||||
const messageBubble = getMessageBubble()
|
||||
const labelText = getLabelText()
|
||||
const messageText = getMessageText()
|
||||
const backgroundGroup = new XmlElement({
|
||||
name: 'g',
|
||||
content: [labelRect, messageBubble],
|
||||
attrs: { stroke: '#d5d5d5' },
|
||||
})
|
||||
const foregroundGroup = new XmlElement({
|
||||
name: 'g',
|
||||
content: [labelText, messageText],
|
||||
attrs: {
|
||||
'aria-hidden': `${!hasLink}`,
|
||||
fill: '#333',
|
||||
'text-anchor': 'middle',
|
||||
'font-family': SOCIAL_FONT_FAMILY,
|
||||
'text-rendering': 'geometricPrecision',
|
||||
'font-weight': 700,
|
||||
'font-size': '110px',
|
||||
'line-height': '14px',
|
||||
},
|
||||
})
|
||||
const logoElement = getLogoElement({
|
||||
logo,
|
||||
horizPadding: labelHorizPadding,
|
||||
badgeHeight: externalHeight,
|
||||
logoWidth,
|
||||
})
|
||||
|
||||
return renderBadge(
|
||||
{
|
||||
links,
|
||||
@@ -540,26 +748,7 @@ function social({
|
||||
accessibleText,
|
||||
height: externalHeight,
|
||||
},
|
||||
`
|
||||
<style>a:hover #llink{fill:url(#b);stroke:#ccc}a:hover #rlink{fill:#4183c4}</style>
|
||||
<linearGradient id="a" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#ccc" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<g stroke="#d5d5d5">
|
||||
<rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="${labelRectWidth}" height="${internalHeight}" rx="2"/>
|
||||
${hasMessage ? renderMessageBubble() : ''}
|
||||
</g>
|
||||
${renderedLogo}
|
||||
<g aria-hidden="${!hasLink}" fill="#333" text-anchor="middle" ${socialFontFamily} text-rendering="geometricPrecision" font-weight="700" font-size="110px" line-height="14px">
|
||||
${renderLabelText()}
|
||||
${hasMessage ? renderMessageText() : ''}
|
||||
</g>
|
||||
`
|
||||
[style, gradients, backgroundGroup, logoElement, foregroundGroup]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -574,7 +763,6 @@ function forTheBadge({
|
||||
}) {
|
||||
const FONT_SIZE = 10
|
||||
const BADGE_HEIGHT = 28
|
||||
const LOGO_HEIGHT = 14
|
||||
const TEXT_MARGIN = 12
|
||||
const LOGO_MARGIN = 9
|
||||
const LOGO_TEXT_GUTTER = 6
|
||||
@@ -641,15 +829,11 @@ function forTheBadge({
|
||||
}
|
||||
}
|
||||
|
||||
const logoElement = new XmlElement({
|
||||
name: 'image',
|
||||
attrs: {
|
||||
x: logoMinX,
|
||||
y: 0.5 * (BADGE_HEIGHT - LOGO_HEIGHT),
|
||||
width: logoWidth,
|
||||
height: LOGO_HEIGHT,
|
||||
'xlink:href': logo,
|
||||
},
|
||||
const logoElement = getLogoElement({
|
||||
logo,
|
||||
horizPadding: logoMinX,
|
||||
badgeHeight: BADGE_HEIGHT,
|
||||
logoWidth,
|
||||
})
|
||||
|
||||
function getLabelElement() {
|
||||
@@ -772,7 +956,7 @@ function forTheBadge({
|
||||
const foregroundGroup = new XmlElement({
|
||||
name: 'g',
|
||||
content: [
|
||||
logo ? logoElement : '',
|
||||
logoElement,
|
||||
hasLabel ? getLabelElement() : '',
|
||||
getMessageElement(),
|
||||
],
|
||||
@@ -794,7 +978,7 @@ function forTheBadge({
|
||||
accessibleText: createAccessibleText({ label, message }),
|
||||
height: BADGE_HEIGHT,
|
||||
},
|
||||
[backgroundGroup.render(), foregroundGroup.render()].join('')
|
||||
[backgroundGroup, foregroundGroup]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -210,6 +210,28 @@ describe('The badge generator', function () {
|
||||
links: ['https://shields.io/', 'https://www.google.co.uk/'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'flat',
|
||||
color: '#000',
|
||||
labelColor: '#f3f3f3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'flat',
|
||||
color: '#e2ffe1',
|
||||
labelColor: '#000',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('"flat-square" template badge generation', function () {
|
||||
@@ -280,6 +302,28 @@ describe('The badge generator', function () {
|
||||
links: ['https://shields.io/', 'https://www.google.co.uk/'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'flat-square',
|
||||
color: '#000',
|
||||
labelColor: '#f3f3f3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'flat-square',
|
||||
color: '#e2ffe1',
|
||||
labelColor: '#000',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('"plastic" template badge generation', function () {
|
||||
@@ -350,6 +394,28 @@ describe('The badge generator', function () {
|
||||
links: ['https://shields.io/', 'https://www.google.co.uk/'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'plastic',
|
||||
color: '#000',
|
||||
labelColor: '#f3f3f3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'plastic',
|
||||
color: '#e2ffe1',
|
||||
labelColor: '#000',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('"for-the-badge" template badge generation', function () {
|
||||
@@ -447,6 +513,28 @@ describe('The badge generator', function () {
|
||||
links: ['https://shields.io/', 'https://www.google.co.uk/'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
color: '#000',
|
||||
labelColor: '#f3f3f3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
color: '#e2ffe1',
|
||||
labelColor: '#000',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('"social" template badge generation', function () {
|
||||
@@ -556,28 +644,4 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('text colors', function () {
|
||||
it('should use black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'flat',
|
||||
color: '#000',
|
||||
labelColor: '#f3f3f3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should use black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
color: '#e2ffe1',
|
||||
labelColor: '#000',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,7 +58,7 @@ class XmlElement {
|
||||
if (this.content.length > 0) {
|
||||
const content = this.content
|
||||
.map(function (el) {
|
||||
if (el instanceof XmlElement) {
|
||||
if (typeof el.render === 'function') {
|
||||
return el.render()
|
||||
} else {
|
||||
return escapeXml(el)
|
||||
@@ -73,4 +73,24 @@ class XmlElement {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { escapeXml, stripXmlWhitespace, XmlElement }
|
||||
/**
|
||||
* Convenience class. Sometimes it is useful to return an object that behaves
|
||||
* like an XmlElement but renders multiple XML tags (not wrapped in a <g>).
|
||||
*/
|
||||
class ElementList {
|
||||
constructor({ content = [] }) {
|
||||
this.content = content
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.content.reduce(
|
||||
(acc, el) =>
|
||||
typeof el.render === 'function'
|
||||
? acc + el.render()
|
||||
: acc + escapeXml(el),
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { escapeXml, stripXmlWhitespace, XmlElement, ElementList }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "badge-maker",
|
||||
"version": "3.3.0",
|
||||
"version": "3.3.1",
|
||||
"description": "Shields.io badge library",
|
||||
"keywords": [
|
||||
"GitHub",
|
||||
@@ -21,13 +21,13 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/badges/shields/issues"
|
||||
},
|
||||
"homepage": "http://shields.io",
|
||||
"homepage": "https://shields.io",
|
||||
"bin": {
|
||||
"badge": "lib/badge-cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10",
|
||||
"npm": ">= 5"
|
||||
"node": ">= 12",
|
||||
"npm": ">= 6"
|
||||
},
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
debug:
|
||||
enabled: 'GITHUB_DEBUG_ENABLED'
|
||||
intervalSeconds: 'GITHUB_DEBUG_INTERVAL_SECONDS'
|
||||
gitlab:
|
||||
authorizedOrigins: 'GITLAB_ORIGINS'
|
||||
jenkins:
|
||||
authorizedOrigins: 'JENKINS_ORIGINS'
|
||||
jira:
|
||||
@@ -52,6 +54,8 @@ public:
|
||||
authorizedOrigins: 'SONAR_ORIGINS'
|
||||
teamcity:
|
||||
authorizedOrigins: 'TEAMCITY_ORIGINS'
|
||||
weblate:
|
||||
authorizedOrigins: 'WEBLATE_ORIGINS'
|
||||
trace: 'TRACE_SERVICES'
|
||||
|
||||
cacheHeaders:
|
||||
@@ -75,6 +79,7 @@ private:
|
||||
gh_client_id: 'GH_CLIENT_ID'
|
||||
gh_client_secret: 'GH_CLIENT_SECRET'
|
||||
gh_token: 'GH_TOKEN'
|
||||
gitlab_token: 'GITLAB_TOKEN'
|
||||
jenkins_user: 'JENKINS_USER'
|
||||
jenkins_pass: 'JENKINS_PASS'
|
||||
jira_user: 'JIRA_USER'
|
||||
@@ -95,4 +100,5 @@ private:
|
||||
wheelmap_token: 'WHEELMAP_TOKEN'
|
||||
influx_username: 'INFLUX_USERNAME'
|
||||
influx_password: 'INFLUX_PASSWORD'
|
||||
weblate_api_key: 'WEBLATE_API_KEY'
|
||||
youtube_api_key: 'YOUTUBE_API_KEY'
|
||||
|
||||
@@ -22,6 +22,8 @@ public:
|
||||
debug:
|
||||
enabled: false
|
||||
intervalSeconds: 200
|
||||
weblate:
|
||||
authorizedOrigins: 'https://hosted.weblate.org'
|
||||
trace: false
|
||||
|
||||
cacheHeaders:
|
||||
|
||||
@@ -3,6 +3,7 @@ private:
|
||||
discord_bot_token: ...
|
||||
gh_client_id: ...
|
||||
gh_client_secret: ...
|
||||
gitlab_token: ...
|
||||
redis_url: ...
|
||||
sentry_dsn: ...
|
||||
shields_secret: ...
|
||||
@@ -10,5 +11,6 @@ private:
|
||||
sl_insight_apiToken: ...
|
||||
twitch_client_id: ...
|
||||
twitch_client_secret: ...
|
||||
weblate_api_key: ...
|
||||
wheelmap_token: ...
|
||||
youtube_api_key: ...
|
||||
|
||||
@@ -5,7 +5,9 @@ private:
|
||||
# you can also set these values through environment variables, which may be
|
||||
# preferable for self hosting.
|
||||
gh_token: '...'
|
||||
gitlab_token: '...'
|
||||
twitch_client_id: '...'
|
||||
twitch_client_secret: '...'
|
||||
weblate_api_key: '...'
|
||||
wheelmap_token: '...'
|
||||
youtube_api_key: '...'
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const { URL } = require('url')
|
||||
const queryString = require('query-string')
|
||||
const { compile } = require('path-to-regexp')
|
||||
// Avoid "Attempted import error: 'URL' is not exported from 'url'" in frontend.
|
||||
import url from 'url'
|
||||
import queryString from 'query-string'
|
||||
import { compile } from 'path-to-regexp'
|
||||
|
||||
function badgeUrlFromPath({
|
||||
baseUrl = '',
|
||||
@@ -147,13 +146,13 @@ function dynamicBadgeUrl({
|
||||
function rasterRedirectUrl({ rasterUrl }, badgeUrl) {
|
||||
// Ensure we're always using the `rasterUrl` by using just the path from
|
||||
// the request URL.
|
||||
const { pathname, search } = new URL(badgeUrl, 'https://bogus.test')
|
||||
const result = new URL(pathname, rasterUrl)
|
||||
const { pathname, search } = new url.URL(badgeUrl, 'https://bogus.test')
|
||||
const result = new url.URL(pathname, rasterUrl)
|
||||
result.search = search
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
badgeUrlFromPath,
|
||||
badgeUrlFromPattern,
|
||||
encodeField,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const { test, given } = require('sazerac')
|
||||
const {
|
||||
import { test, given } from 'sazerac'
|
||||
import {
|
||||
badgeUrlFromPath,
|
||||
badgeUrlFromPattern,
|
||||
encodeField,
|
||||
staticBadgeUrl,
|
||||
queryStringStaticBadgeUrl,
|
||||
dynamicBadgeUrl,
|
||||
} = require('./make-badge-url')
|
||||
} from './make-badge-url.js'
|
||||
|
||||
describe('Badge URL generation functions', function () {
|
||||
test(badgeUrlFromPath, () => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use strict'
|
||||
|
||||
// Escapes `t` using the format specified in
|
||||
// <https://github.com/espadrine/gh-badges/issues/12#issuecomment-31518129>
|
||||
function escapeFormat(t) {
|
||||
@@ -13,6 +11,4 @@ function escapeFormat(t) {
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
escapeFormat,
|
||||
}
|
||||
export { escapeFormat }
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { test, given } = require('sazerac')
|
||||
const { escapeFormat } = require('./path-helpers')
|
||||
import { test, given } from 'sazerac'
|
||||
import { escapeFormat } from './path-helpers.js'
|
||||
|
||||
describe('Badge URL helper functions', function () {
|
||||
test(escapeFormat, () => {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { URL } = require('url')
|
||||
const { InvalidParameter } = require('./errors')
|
||||
import { URL } from 'url'
|
||||
import { InvalidParameter } from './errors.js'
|
||||
|
||||
class AuthHelper {
|
||||
constructor(
|
||||
@@ -207,4 +205,4 @@ class AuthHelper {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { AuthHelper }
|
||||
export { AuthHelper }
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { test, given, forCases } = require('sazerac')
|
||||
const { AuthHelper } = require('./auth-helper')
|
||||
const { InvalidParameter } = require('./errors')
|
||||
import { expect } from 'chai'
|
||||
import { test, given, forCases } from 'sazerac'
|
||||
import { AuthHelper } from './auth-helper.js'
|
||||
import { InvalidParameter } from './errors.js'
|
||||
|
||||
describe('AuthHelper', function () {
|
||||
describe('constructor checks', function () {
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { print } = require('graphql/language/printer')
|
||||
const BaseService = require('./base')
|
||||
const { InvalidResponse, ShieldsRuntimeError } = require('./errors')
|
||||
const { parseJson } = require('./json')
|
||||
import { print } from 'graphql/language/printer.js'
|
||||
import BaseService from './base.js'
|
||||
import { InvalidResponse, ShieldsRuntimeError } from './errors.js'
|
||||
import { parseJson } from './json.js'
|
||||
|
||||
function defaultTransformErrors(errors) {
|
||||
return new InvalidResponse({ prettyMessage: errors[0].message })
|
||||
@@ -93,4 +91,4 @@ class BaseGraphqlService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseGraphqlService
|
||||
export default BaseGraphqlService
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const gql = require('graphql-tag')
|
||||
const sinon = require('sinon')
|
||||
const BaseGraphqlService = require('./base-graphql')
|
||||
const { InvalidResponse } = require('./errors')
|
||||
import Joi from 'joi'
|
||||
import { expect } from 'chai'
|
||||
import gql from 'graphql-tag'
|
||||
import sinon from 'sinon'
|
||||
import BaseGraphqlService from './base-graphql.js'
|
||||
import { InvalidResponse } from './errors.js'
|
||||
|
||||
const dummySchema = Joi.object({
|
||||
requiredString: Joi.string().required(),
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const BaseService = require('./base')
|
||||
const { parseJson } = require('./json')
|
||||
import BaseService from './base.js'
|
||||
import { parseJson } from './json.js'
|
||||
|
||||
/**
|
||||
* Services which query a JSON endpoint should extend BaseJsonService
|
||||
@@ -54,4 +52,4 @@ class BaseJsonService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseJsonService
|
||||
export default BaseJsonService
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const BaseJsonService = require('./base-json')
|
||||
import Joi from 'joi'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import BaseJsonService from './base-json.js'
|
||||
|
||||
const dummySchema = Joi.object({
|
||||
requiredString: Joi.string().required(),
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const makeBadge = require('../../badge-maker/lib/make-badge')
|
||||
const BaseService = require('./base')
|
||||
const {
|
||||
import makeBadge from '../../badge-maker/lib/make-badge.js'
|
||||
import BaseService from './base.js'
|
||||
import {
|
||||
serverHasBeenUpSinceResourceCached,
|
||||
setCacheHeadersForStaticResource,
|
||||
} = require('./cache-headers')
|
||||
const { makeSend } = require('./legacy-result-sender')
|
||||
const { MetricHelper } = require('./metric-helper')
|
||||
const coalesceBadge = require('./coalesce-badge')
|
||||
const { prepareRoute, namedParamsForMatch } = require('./route')
|
||||
} from './cache-headers.js'
|
||||
import { makeSend } from './legacy-result-sender.js'
|
||||
import { MetricHelper } from './metric-helper.js'
|
||||
import coalesceBadge from './coalesce-badge.js'
|
||||
import { prepareRoute, namedParamsForMatch } from './route.js'
|
||||
|
||||
module.exports = class BaseStaticService extends BaseService {
|
||||
export default class BaseStaticService extends BaseService {
|
||||
static register({ camp, metricInstance }, serviceConfig) {
|
||||
const { regex, captureNames } = prepareRoute(this.route)
|
||||
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
// See available emoji at http://emoji.muan.co/
|
||||
const emojic = require('emojic')
|
||||
const BaseService = require('./base')
|
||||
const trace = require('./trace')
|
||||
const { InvalidResponse } = require('./errors')
|
||||
import emojic from 'emojic'
|
||||
import BaseService from './base.js'
|
||||
import trace from './trace.js'
|
||||
import { InvalidResponse } from './errors.js'
|
||||
|
||||
const defaultValueMatcher = />([^<>]+)<\/text><\/g>/
|
||||
const leadingWhitespace = /(?:\r\n\s*|\r\s*|\n\s*)/g
|
||||
@@ -90,4 +88,4 @@ class BaseSvgScrapingService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseSvgScrapingService
|
||||
export default BaseSvgScrapingService
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const Joi = require('joi')
|
||||
const makeBadge = require('../../badge-maker/lib/make-badge')
|
||||
const BaseSvgScrapingService = require('./base-svg-scraping')
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import Joi from 'joi'
|
||||
import makeBadge from '../../badge-maker/lib/make-badge.js'
|
||||
import BaseSvgScrapingService from './base-svg-scraping.js'
|
||||
|
||||
const schema = Joi.object({
|
||||
message: Joi.string().required(),
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
// See available emoji at http://emoji.muan.co/
|
||||
const emojic = require('emojic')
|
||||
const fastXmlParser = require('fast-xml-parser')
|
||||
const BaseService = require('./base')
|
||||
const trace = require('./trace')
|
||||
const { InvalidResponse } = require('./errors')
|
||||
import emojic from 'emojic'
|
||||
import fastXmlParser from 'fast-xml-parser'
|
||||
import BaseService from './base.js'
|
||||
import trace from './trace.js'
|
||||
import { InvalidResponse } from './errors.js'
|
||||
|
||||
/**
|
||||
* Services which query a XML endpoint should extend BaseXmlService
|
||||
@@ -68,4 +66,4 @@ class BaseXmlService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseXmlService
|
||||
export default BaseXmlService
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const BaseXmlService = require('./base-xml')
|
||||
import Joi from 'joi'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import BaseXmlService from './base-xml.js'
|
||||
|
||||
const dummySchema = Joi.object({
|
||||
requiredString: Joi.string().required(),
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const emojic = require('emojic')
|
||||
const yaml = require('js-yaml')
|
||||
const BaseService = require('./base')
|
||||
const { InvalidResponse } = require('./errors')
|
||||
const trace = require('./trace')
|
||||
import emojic from 'emojic'
|
||||
import yaml from 'js-yaml'
|
||||
import BaseService from './base.js'
|
||||
import { InvalidResponse } from './errors.js'
|
||||
import trace from './trace.js'
|
||||
|
||||
/**
|
||||
* Services which query a YAML endpoint should extend BaseYamlService
|
||||
@@ -72,4 +70,4 @@ class BaseYamlService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseYamlService
|
||||
export default BaseYamlService
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const BaseYamlService = require('./base-yaml')
|
||||
import Joi from 'joi'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import BaseYamlService from './base-yaml.js'
|
||||
|
||||
const dummySchema = Joi.object({
|
||||
requiredString: Joi.string().required(),
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
'use strict'
|
||||
/**
|
||||
* @module
|
||||
*/
|
||||
|
||||
// See available emoji at http://emoji.muan.co/
|
||||
const emojic = require('emojic')
|
||||
const Joi = require('joi')
|
||||
const log = require('../server/log')
|
||||
const { AuthHelper } = require('./auth-helper')
|
||||
const { MetricHelper, MetricNames } = require('./metric-helper')
|
||||
const { assertValidCategory } = require('./categories')
|
||||
const checkErrorResponse = require('./check-error-response')
|
||||
const coalesceBadge = require('./coalesce-badge')
|
||||
const {
|
||||
import emojic from 'emojic'
|
||||
import Joi from 'joi'
|
||||
import log from '../server/log.js'
|
||||
import { AuthHelper } from './auth-helper.js'
|
||||
import { MetricHelper, MetricNames } from './metric-helper.js'
|
||||
import { assertValidCategory } from './categories.js'
|
||||
import checkErrorResponse from './check-error-response.js'
|
||||
import coalesceBadge from './coalesce-badge.js'
|
||||
import {
|
||||
NotFound,
|
||||
InvalidResponse,
|
||||
Inaccessible,
|
||||
ImproperlyConfigured,
|
||||
InvalidParameter,
|
||||
Deprecated,
|
||||
} = require('./errors')
|
||||
const { validateExample, transformExample } = require('./examples')
|
||||
const {
|
||||
} from './errors.js'
|
||||
import { validateExample, transformExample } from './examples.js'
|
||||
import { fetchFactory } from './got.js'
|
||||
import {
|
||||
makeFullUrl,
|
||||
assertValidRoute,
|
||||
prepareRoute,
|
||||
namedParamsForMatch,
|
||||
getQueryParamNames,
|
||||
} = require('./route')
|
||||
const { assertValidServiceDefinition } = require('./service-definitions')
|
||||
const trace = require('./trace')
|
||||
const validate = require('./validate')
|
||||
} from './route.js'
|
||||
import { assertValidServiceDefinition } from './service-definitions.js'
|
||||
import trace from './trace.js'
|
||||
import validate from './validate.js'
|
||||
|
||||
const defaultBadgeDataSchema = Joi.object({
|
||||
label: Joi.string(),
|
||||
@@ -143,6 +143,8 @@ class BaseService {
|
||||
license: 3600,
|
||||
version: 300,
|
||||
debug: 60,
|
||||
downloads: 900,
|
||||
social: 900,
|
||||
}
|
||||
return cacheLengths[this.category]
|
||||
}
|
||||
@@ -430,6 +432,8 @@ class BaseService {
|
||||
ServiceClass: this,
|
||||
})
|
||||
|
||||
const fetcher = fetchFactory(fetchLimitBytes)
|
||||
|
||||
camp.route(
|
||||
regex,
|
||||
handleRequest(cacheHeaderConfig, {
|
||||
@@ -440,7 +444,7 @@ class BaseService {
|
||||
const namedParams = namedParamsForMatch(captureNames, match, this)
|
||||
const serviceData = await this.invoke(
|
||||
{
|
||||
sendAndCacheRequest: request.asPromise,
|
||||
sendAndCacheRequest: fetcher,
|
||||
sendAndCacheRequestWithCallbacks: request,
|
||||
githubApiProvider,
|
||||
metricHelper,
|
||||
@@ -563,4 +567,4 @@ class BaseService {
|
||||
* An HTML string that is included in the badge popup.
|
||||
*/
|
||||
|
||||
module.exports = BaseService
|
||||
export default BaseService
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const chai = require('chai')
|
||||
const { expect } = chai
|
||||
const sinon = require('sinon')
|
||||
const prometheus = require('prom-client')
|
||||
const PrometheusMetrics = require('../server/prometheus-metrics')
|
||||
const trace = require('./trace')
|
||||
const {
|
||||
import Joi from 'joi'
|
||||
import chai from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import prometheus from 'prom-client'
|
||||
import chaiAsPromised from 'chai-as-promised'
|
||||
import PrometheusMetrics from '../server/prometheus-metrics.js'
|
||||
import trace from './trace.js'
|
||||
import {
|
||||
NotFound,
|
||||
Inaccessible,
|
||||
InvalidResponse,
|
||||
InvalidParameter,
|
||||
Deprecated,
|
||||
} = require('./errors')
|
||||
const BaseService = require('./base')
|
||||
const { MetricHelper, MetricNames } = require('./metric-helper')
|
||||
require('../register-chai-plugins.spec')
|
||||
chai.use(require('chai-as-promised'))
|
||||
} from './errors.js'
|
||||
import BaseService from './base.js'
|
||||
import { MetricHelper, MetricNames } from './metric-helper.js'
|
||||
import '../register-chai-plugins.spec.js'
|
||||
const { expect } = chai
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
queryParamA: Joi.string(),
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const Joi = require('joi')
|
||||
const coalesce = require('./coalesce')
|
||||
import assert from 'assert'
|
||||
import Joi from 'joi'
|
||||
import coalesce from './coalesce.js'
|
||||
|
||||
const serverStartTimeGMTString = new Date().toGMTString()
|
||||
const serverStartTimestamp = Date.now()
|
||||
@@ -104,7 +102,7 @@ function serverHasBeenUpSinceResourceCached(req) {
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
coalesceCacheLength,
|
||||
setCacheHeaders,
|
||||
setHeadersForCacheLength,
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
'use strict'
|
||||
|
||||
const { test, given } = require('sazerac')
|
||||
const chai = require('chai')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const httpMocks = require('node-mocks-http')
|
||||
const {
|
||||
import { test, given } from 'sazerac'
|
||||
import chai, { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import httpMocks from 'node-mocks-http'
|
||||
import chaiDatetime from 'chai-datetime'
|
||||
import {
|
||||
coalesceCacheLength,
|
||||
setHeadersForCacheLength,
|
||||
setCacheHeaders,
|
||||
setCacheHeadersForStaticResource,
|
||||
serverHasBeenUpSinceResourceCached,
|
||||
} = require('./cache-headers')
|
||||
|
||||
chai.use(require('chai-datetime'))
|
||||
} from './cache-headers.js'
|
||||
chai.use(chaiDatetime)
|
||||
|
||||
describe('Cache header functions', function () {
|
||||
let res
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const categories = require('../../services/categories')
|
||||
import Joi from 'joi'
|
||||
import categories from '../../services/categories.js'
|
||||
|
||||
const isRealCategory = Joi.equal(...categories.map(({ id }) => id)).required()
|
||||
|
||||
@@ -13,7 +11,4 @@ function assertValidCategory(category, message = undefined) {
|
||||
Joi.assert(category, isValidCategory, message)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isValidCategory,
|
||||
assertValidCategory,
|
||||
}
|
||||
export { isValidCategory, assertValidCategory }
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
const { NotFound, InvalidResponse, Inaccessible } = require('./errors')
|
||||
import { NotFound, InvalidResponse, Inaccessible } from './errors.js'
|
||||
|
||||
const defaultErrorMessages = {
|
||||
404: 'not found',
|
||||
}
|
||||
|
||||
module.exports = function checkErrorResponse(errorMessages = {}) {
|
||||
export default function checkErrorResponse(errorMessages = {}) {
|
||||
return async function ({ buffer, res }) {
|
||||
let error
|
||||
errorMessages = { ...defaultErrorMessages, ...errorMessages }
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { NotFound, InvalidResponse, Inaccessible } = require('./errors')
|
||||
const checkErrorResponse = require('./check-error-response')
|
||||
import { expect } from 'chai'
|
||||
import { NotFound, InvalidResponse, Inaccessible } from './errors.js'
|
||||
import checkErrorResponse from './check-error-response.js'
|
||||
|
||||
describe('async error handler', function () {
|
||||
const buffer = Buffer.from('some stuff')
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
import {
|
||||
decodeDataUrlFromQueryParam,
|
||||
prepareNamedLogo,
|
||||
} = require('../../lib/logos')
|
||||
const { svg2base64 } = require('../../lib/svg-helpers')
|
||||
const coalesce = require('./coalesce')
|
||||
const toArray = require('./to-array')
|
||||
} from '../../lib/logos.js'
|
||||
import { svg2base64 } from '../../lib/svg-helpers.js'
|
||||
import coalesce from './coalesce.js'
|
||||
import toArray from './to-array.js'
|
||||
|
||||
// Translate modern badge data to the legacy schema understood by the badge
|
||||
// maker. Allow the user to override the label, color, logo, etc. through the
|
||||
@@ -34,7 +32,7 @@ const toArray = require('./to-array')
|
||||
// 3. In the case of the `social` style only, the last precedence is the
|
||||
// service's default logo. The `logoColor` can be overridden by the query
|
||||
// string.
|
||||
module.exports = function coalesceBadge(
|
||||
export default function coalesceBadge(
|
||||
overrides,
|
||||
serviceData,
|
||||
// These two parameters were kept separate to make tests clearer.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { getShieldsIcon, getSimpleIcon } = require('../../lib/logos')
|
||||
const coalesceBadge = require('./coalesce-badge')
|
||||
import { expect } from 'chai'
|
||||
import { getShieldsIcon, getSimpleIcon } from '../../lib/logos.js'
|
||||
import coalesceBadge from './coalesce-badge.js'
|
||||
|
||||
describe('coalesceBadge', function () {
|
||||
describe('Label', function () {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function coalesce(...candidates) {
|
||||
export default function coalesce(...candidates) {
|
||||
return candidates.find(c => c !== undefined && c !== null)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { test, given } = require('sazerac')
|
||||
const coalesce = require('./coalesce')
|
||||
import { test, given } from 'sazerac'
|
||||
import coalesce from './coalesce.js'
|
||||
|
||||
// Sticking with our one-line spread implementation, and defaulting to
|
||||
// `undefined` instead of `null`, though h/t to
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const camelcase = require('camelcase')
|
||||
const BaseService = require('./base')
|
||||
const { isValidCategory } = require('./categories')
|
||||
const { Deprecated } = require('./errors')
|
||||
const { isValidRoute } = require('./route')
|
||||
import Joi from 'joi'
|
||||
import camelcase from 'camelcase'
|
||||
import BaseService from './base.js'
|
||||
import { isValidCategory } from './categories.js'
|
||||
import { Deprecated } from './errors.js'
|
||||
import { isValidRoute } from './route.js'
|
||||
|
||||
const attrSchema = Joi.object({
|
||||
route: isValidRoute,
|
||||
@@ -44,4 +42,4 @@ function deprecatedService(attrs) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = deprecatedService
|
||||
export default deprecatedService
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const deprecatedService = require('./deprecated-service')
|
||||
import { expect } from 'chai'
|
||||
import deprecatedService from './deprecated-service.js'
|
||||
|
||||
describe('DeprecatedService', function () {
|
||||
const route = {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Base error class
|
||||
*
|
||||
@@ -210,7 +208,7 @@ class Deprecated extends ShieldsRuntimeError {
|
||||
* badge when we catch and render the exception (Optional)
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
ShieldsRuntimeError,
|
||||
NotFound,
|
||||
ImproperlyConfigured,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { pathToRegexp, compile } = require('path-to-regexp')
|
||||
const categories = require('../../services/categories')
|
||||
const coalesceBadge = require('./coalesce-badge')
|
||||
const { makeFullUrl } = require('./route')
|
||||
import Joi from 'joi'
|
||||
import { pathToRegexp, compile } from 'path-to-regexp'
|
||||
import categories from '../../services/categories.js'
|
||||
import coalesceBadge from './coalesce-badge.js'
|
||||
import { makeFullUrl } from './route.js'
|
||||
|
||||
const optionalObjectOfKeyValues = Joi.object().pattern(
|
||||
/./,
|
||||
@@ -155,7 +153,4 @@ function transformExample(inExample, index, ServiceClass) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateExample,
|
||||
transformExample,
|
||||
}
|
||||
export { validateExample, transformExample }
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { test, given } = require('sazerac')
|
||||
const { validateExample, transformExample } = require('./examples')
|
||||
import { expect } from 'chai'
|
||||
import { test, given } from 'sazerac'
|
||||
import { validateExample, transformExample } from './examples.js'
|
||||
|
||||
describe('validateExample function', function () {
|
||||
it('passes valid examples', function () {
|
||||
|
||||
96
core/base-service/got.js
Normal file
96
core/base-service/got.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import got from 'got'
|
||||
import { Inaccessible, InvalidResponse } from './errors.js'
|
||||
|
||||
const userAgent = 'Shields.io/2003a'
|
||||
|
||||
function requestOptions2GotOptions(options) {
|
||||
const requestOptions = Object.assign({}, options)
|
||||
const gotOptions = {}
|
||||
const interchangableOptions = ['body', 'form', 'headers', 'method', 'url']
|
||||
|
||||
interchangableOptions.forEach(function (opt) {
|
||||
if (opt in requestOptions) {
|
||||
gotOptions[opt] = requestOptions[opt]
|
||||
delete requestOptions[opt]
|
||||
}
|
||||
})
|
||||
|
||||
if ('qs' in requestOptions) {
|
||||
gotOptions.searchParams = requestOptions.qs
|
||||
delete requestOptions.qs
|
||||
}
|
||||
|
||||
if ('gzip' in requestOptions) {
|
||||
gotOptions.decompress = requestOptions.gzip
|
||||
delete requestOptions.gzip
|
||||
}
|
||||
|
||||
if ('strictSSL' in requestOptions) {
|
||||
gotOptions.https = {
|
||||
rejectUnauthorized: requestOptions.strictSSL,
|
||||
}
|
||||
delete requestOptions.strictSSL
|
||||
}
|
||||
|
||||
if ('auth' in requestOptions) {
|
||||
gotOptions.username = requestOptions.auth.user
|
||||
gotOptions.password = requestOptions.auth.pass
|
||||
delete requestOptions.auth
|
||||
}
|
||||
|
||||
if (Object.keys(requestOptions).length > 0) {
|
||||
throw new Error(`Found unrecognised options ${Object.keys(requestOptions)}`)
|
||||
}
|
||||
|
||||
return gotOptions
|
||||
}
|
||||
|
||||
async function sendRequest(gotWrapper, url, options) {
|
||||
const gotOptions = requestOptions2GotOptions(options)
|
||||
gotOptions.throwHttpErrors = false
|
||||
gotOptions.retry = 0
|
||||
gotOptions.headers = gotOptions.headers || {}
|
||||
gotOptions.headers['User-Agent'] = userAgent
|
||||
try {
|
||||
const resp = await gotWrapper(url, gotOptions)
|
||||
return { res: resp, buffer: resp.body }
|
||||
} catch (err) {
|
||||
if (err instanceof got.CancelError) {
|
||||
throw new InvalidResponse({
|
||||
underlyingError: new Error('Maximum response size exceeded'),
|
||||
})
|
||||
}
|
||||
throw new Inaccessible({ underlyingError: err })
|
||||
}
|
||||
}
|
||||
|
||||
function fetchFactory(fetchLimitBytes) {
|
||||
const gotWithLimit = got.extend({
|
||||
handlers: [
|
||||
(options, next) => {
|
||||
const promiseOrStream = next(options)
|
||||
promiseOrStream.on('downloadProgress', progress => {
|
||||
if (
|
||||
progress.transferred > fetchLimitBytes &&
|
||||
// just accept the file if we've already finished downloading
|
||||
// the entire file before we went over the limit
|
||||
progress.percent !== 1
|
||||
) {
|
||||
/*
|
||||
TODO: we should be able to pass cancel() a message
|
||||
https://github.com/sindresorhus/got/blob/main/documentation/advanced-creation.md#examples
|
||||
but by the time we catch it, err.message is just "Promise was canceled"
|
||||
*/
|
||||
promiseOrStream.cancel('Maximum response size exceeded')
|
||||
}
|
||||
})
|
||||
|
||||
return promiseOrStream
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
return sendRequest.bind(sendRequest, gotWithLimit)
|
||||
}
|
||||
|
||||
export { requestOptions2GotOptions, fetchFactory }
|
||||
102
core/base-service/got.spec.js
Normal file
102
core/base-service/got.spec.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import { expect } from 'chai'
|
||||
import nock from 'nock'
|
||||
import { requestOptions2GotOptions, fetchFactory } from './got.js'
|
||||
import { Inaccessible, InvalidResponse } from './errors.js'
|
||||
|
||||
describe('requestOptions2GotOptions function', function () {
|
||||
it('translates valid options', function () {
|
||||
expect(
|
||||
requestOptions2GotOptions({
|
||||
body: 'body',
|
||||
form: 'form',
|
||||
headers: 'headers',
|
||||
method: 'method',
|
||||
url: 'url',
|
||||
qs: 'qs',
|
||||
gzip: 'gzip',
|
||||
strictSSL: 'strictSSL',
|
||||
auth: { user: 'user', pass: 'pass' },
|
||||
})
|
||||
).to.deep.equal({
|
||||
body: 'body',
|
||||
form: 'form',
|
||||
headers: 'headers',
|
||||
method: 'method',
|
||||
url: 'url',
|
||||
searchParams: 'qs',
|
||||
decompress: 'gzip',
|
||||
https: { rejectUnauthorized: 'strictSSL' },
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
})
|
||||
})
|
||||
|
||||
it('throws if unrecognised options are found', function () {
|
||||
expect(() =>
|
||||
requestOptions2GotOptions({ body: 'body', foobar: 'foobar' })
|
||||
).to.throw(Error, 'Found unrecognised options foobar')
|
||||
})
|
||||
})
|
||||
|
||||
describe('got wrapper', function () {
|
||||
it('should not throw an error if the response <= fetchLimitBytes', async function () {
|
||||
nock('https://www.google.com')
|
||||
.get('/foo/bar')
|
||||
.once()
|
||||
.reply(200, 'x'.repeat(100))
|
||||
const sendRequest = fetchFactory(100)
|
||||
const { res } = await sendRequest('https://www.google.com/foo/bar')
|
||||
expect(res.statusCode).to.equal(200)
|
||||
})
|
||||
|
||||
it('should throw an InvalidResponse error if the response is > fetchLimitBytes', async function () {
|
||||
nock('https://www.google.com')
|
||||
.get('/foo/bar')
|
||||
.once()
|
||||
.reply(200, 'x'.repeat(101))
|
||||
const sendRequest = fetchFactory(100)
|
||||
return expect(
|
||||
sendRequest('https://www.google.com/foo/bar')
|
||||
).to.be.rejectedWith(InvalidResponse, 'Maximum response size exceeded')
|
||||
})
|
||||
|
||||
it('should throw an Inaccessible error if the request throws a (non-HTTP) error', async function () {
|
||||
nock('https://www.google.com').get('/foo/bar').replyWithError('oh no')
|
||||
const sendRequest = fetchFactory(1024)
|
||||
return expect(
|
||||
sendRequest('https://www.google.com/foo/bar')
|
||||
).to.be.rejectedWith(Inaccessible, 'oh no')
|
||||
})
|
||||
|
||||
it('should throw an Inaccessible error if the host can not be accessed', async function () {
|
||||
this.timeout(5000)
|
||||
nock.disableNetConnect()
|
||||
const sendRequest = fetchFactory(1024)
|
||||
return expect(
|
||||
sendRequest('https://www.google.com/foo/bar')
|
||||
).to.be.rejectedWith(
|
||||
Inaccessible,
|
||||
'Nock: Disallowed net connect for "www.google.com:443/foo/bar"'
|
||||
)
|
||||
})
|
||||
|
||||
it('should pass a custom user agent header', async function () {
|
||||
nock('https://www.google.com', {
|
||||
reqheaders: {
|
||||
'user-agent': function (agent) {
|
||||
return agent.startsWith('Shields.io')
|
||||
},
|
||||
},
|
||||
})
|
||||
.get('/foo/bar')
|
||||
.once()
|
||||
.reply(200)
|
||||
const sendRequest = fetchFactory(1024)
|
||||
await sendRequest('https://www.google.com/foo/bar')
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
nock.cleanAll()
|
||||
nock.enableNetConnect()
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,3 @@
|
||||
'use strict'
|
||||
/**
|
||||
* @module
|
||||
*/
|
||||
@@ -49,4 +48,4 @@ function mergeQueries(...queries) {
|
||||
return merged
|
||||
}
|
||||
|
||||
module.exports = { mergeQueries }
|
||||
export { mergeQueries }
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'use strict'
|
||||
import { expect } from 'chai'
|
||||
import gql from 'graphql-tag'
|
||||
import { print } from 'graphql/language/printer.js'
|
||||
import { mergeQueries } from './graphql.js'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const gql = require('graphql-tag')
|
||||
const { print } = require('graphql/language/printer')
|
||||
const { mergeQueries } = require('./graphql')
|
||||
|
||||
require('../register-chai-plugins.spec')
|
||||
import '../register-chai-plugins.spec.js'
|
||||
|
||||
describe('mergeQueries function', function () {
|
||||
it('merges valid gql queries', function () {
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
'use strict'
|
||||
|
||||
const BaseService = require('./base')
|
||||
const BaseJsonService = require('./base-json')
|
||||
const BaseGraphqlService = require('./base-graphql')
|
||||
const BaseStaticService = require('./base-static')
|
||||
const BaseSvgScrapingService = require('./base-svg-scraping')
|
||||
const BaseXmlService = require('./base-xml')
|
||||
const BaseYamlService = require('./base-yaml')
|
||||
const deprecatedService = require('./deprecated-service')
|
||||
const redirector = require('./redirector')
|
||||
const {
|
||||
import BaseService from './base.js'
|
||||
import BaseJsonService from './base-json.js'
|
||||
import BaseGraphqlService from './base-graphql.js'
|
||||
import BaseStaticService from './base-static.js'
|
||||
import BaseSvgScrapingService from './base-svg-scraping.js'
|
||||
import BaseXmlService from './base-xml.js'
|
||||
import BaseYamlService from './base-yaml.js'
|
||||
import deprecatedService from './deprecated-service.js'
|
||||
import redirector from './redirector.js'
|
||||
import {
|
||||
NotFound,
|
||||
InvalidResponse,
|
||||
Inaccessible,
|
||||
InvalidParameter,
|
||||
Deprecated,
|
||||
} = require('./errors')
|
||||
} from './errors.js'
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
BaseService,
|
||||
BaseJsonService,
|
||||
BaseGraphqlService,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
// See available emoji at http://emoji.muan.co/
|
||||
const emojic = require('emojic')
|
||||
const { InvalidResponse } = require('./errors')
|
||||
const trace = require('./trace')
|
||||
import emojic from 'emojic'
|
||||
import { InvalidResponse } from './errors.js'
|
||||
import trace from './trace.js'
|
||||
|
||||
function parseJson(buffer) {
|
||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||
@@ -23,6 +21,4 @@ function parseJson(buffer) {
|
||||
return json
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseJson,
|
||||
}
|
||||
export { parseJson }
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const request = require('request')
|
||||
const makeBadge = require('../../badge-maker/lib/make-badge')
|
||||
const { setCacheHeaders } = require('./cache-headers')
|
||||
const {
|
||||
Inaccessible,
|
||||
InvalidResponse,
|
||||
ShieldsRuntimeError,
|
||||
} = require('./errors')
|
||||
const { makeSend } = require('./legacy-result-sender')
|
||||
const coalesceBadge = require('./coalesce-badge')
|
||||
import request from 'request'
|
||||
import makeBadge from '../../badge-maker/lib/make-badge.js'
|
||||
import { setCacheHeaders } from './cache-headers.js'
|
||||
import { Inaccessible, InvalidResponse, ShieldsRuntimeError } from './errors.js'
|
||||
import { makeSend } from './legacy-result-sender.js'
|
||||
import coalesceBadge from './coalesce-badge.js'
|
||||
|
||||
const userAgent = 'Shields.io/2003a'
|
||||
|
||||
@@ -206,8 +200,4 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleRequest,
|
||||
promisify,
|
||||
userAgent,
|
||||
}
|
||||
export { handleRequest, promisify, userAgent }
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const nock = require('nock')
|
||||
const portfinder = require('portfinder')
|
||||
const Camp = require('@shields_io/camp')
|
||||
const got = require('../got-test-client')
|
||||
const coalesceBadge = require('./coalesce-badge')
|
||||
const { handleRequest } = require('./legacy-request-handler')
|
||||
import { expect } from 'chai'
|
||||
import nock from 'nock'
|
||||
import portfinder from 'portfinder'
|
||||
import Camp from '@shields_io/camp'
|
||||
import got from '../got-test-client.js'
|
||||
import coalesceBadge from './coalesce-badge.js'
|
||||
import { handleRequest } from './legacy-request-handler.js'
|
||||
|
||||
async function performTwoRequests(baseUrl, first, second) {
|
||||
expect((await got(`${baseUrl}${first}`)).statusCode).to.equal(200)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
const stream = require('stream')
|
||||
import stream from 'stream'
|
||||
|
||||
function streamFromString(str) {
|
||||
const newStream = new stream.Readable()
|
||||
@@ -32,6 +30,4 @@ function makeSend(format, askres, end) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
makeSend,
|
||||
}
|
||||
export { makeSend }
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = []
|
||||
export default []
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
/* eslint-disable */
|
||||
'use strict'
|
||||
|
||||
class BadService {}
|
||||
class BadService {} // lgtm [js/unused-local-variable]
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {}
|
||||
export {}
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = undefined
|
||||
export default undefined
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
const BaseJsonService = require('../base-json')
|
||||
import BaseJsonService from '../base-json.js'
|
||||
|
||||
class BadBaseService {}
|
||||
class GoodService extends BaseJsonService {}
|
||||
class GoodService extends BaseJsonService {
|
||||
static category = 'build'
|
||||
static route = { base: 'it/is', pattern: 'good' }
|
||||
}
|
||||
class BadService extends BadBaseService {}
|
||||
|
||||
module.exports = [GoodService, BadService]
|
||||
export default [GoodService, BadService]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use strict'
|
||||
|
||||
class BadService {}
|
||||
|
||||
module.exports = BadService
|
||||
export default BadService
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
class BadBaseService {}
|
||||
class BadService extends BadBaseService {}
|
||||
|
||||
module.exports = BadService
|
||||
export default BadService
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
const BaseJsonService = require('../base-json')
|
||||
import BaseJsonService from '../base-json.js'
|
||||
|
||||
class GoodServiceOne extends BaseJsonService {
|
||||
static category = 'build'
|
||||
@@ -11,4 +9,4 @@ class GoodServiceTwo extends BaseJsonService {
|
||||
static route = { base: 'good', pattern: 'two' }
|
||||
}
|
||||
|
||||
module.exports = [GoodServiceOne, GoodServiceTwo]
|
||||
export default [GoodServiceOne, GoodServiceTwo]
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const BaseJsonService = require('../base-json')
|
||||
import BaseJsonService from '../base-json.js'
|
||||
|
||||
class GoodService extends BaseJsonService {
|
||||
static category = 'build'
|
||||
static route = { base: 'it/is', pattern: 'good' }
|
||||
}
|
||||
|
||||
module.exports = GoodService
|
||||
export default GoodService
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
const BaseJsonService = require('../base-json')
|
||||
import BaseJsonService from '../base-json.js'
|
||||
|
||||
class GoodServiceOne extends BaseJsonService {
|
||||
static category = 'build'
|
||||
@@ -11,4 +9,4 @@ class GoodServiceTwo extends BaseJsonService {
|
||||
static route = { base: 'good', pattern: 'two' }
|
||||
}
|
||||
|
||||
module.exports = { GoodServiceOne, GoodServiceTwo }
|
||||
export { GoodServiceOne, GoodServiceTwo }
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
'use strict'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import glob from 'glob'
|
||||
import countBy from 'lodash.countby'
|
||||
import categories from '../../services/categories.js'
|
||||
import BaseService from './base.js'
|
||||
import { assertValidServiceDefinitionExport } from './service-definitions.js'
|
||||
|
||||
const path = require('path')
|
||||
const glob = require('glob')
|
||||
const countBy = require('lodash.countby')
|
||||
const categories = require('../../services/categories')
|
||||
const BaseService = require('./base')
|
||||
const { assertValidServiceDefinitionExport } = require('./service-definitions')
|
||||
|
||||
const serviceDir = path.join(__dirname, '..', '..', 'services')
|
||||
const serviceDir = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'..',
|
||||
'services'
|
||||
)
|
||||
|
||||
class InvalidService extends Error {
|
||||
constructor(message) {
|
||||
@@ -16,54 +20,38 @@ class InvalidService extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function loadServiceClasses(servicePaths) {
|
||||
async function loadServiceClasses(servicePaths) {
|
||||
if (!servicePaths) {
|
||||
servicePaths = glob.sync(path.join(serviceDir, '**', '*.service.js'))
|
||||
}
|
||||
|
||||
let serviceClasses = []
|
||||
servicePaths.forEach(servicePath => {
|
||||
const module = require(servicePath)
|
||||
const serviceClasses = []
|
||||
for await (const servicePath of servicePaths) {
|
||||
const currentServiceClasses = Object.values(
|
||||
await import(`file://${servicePath}`)
|
||||
).flatMap(element =>
|
||||
typeof element === 'object' ? Object.values(element) : element
|
||||
)
|
||||
|
||||
const theseServiceClasses = []
|
||||
if (
|
||||
!module ||
|
||||
(module.constructor === Array && module.length === 0) ||
|
||||
(module.constructor === Object && Object.keys(module).length === 0)
|
||||
) {
|
||||
if (currentServiceClasses.length === 0) {
|
||||
throw new InvalidService(
|
||||
`Expected ${servicePath} to export a service or a collection of services`
|
||||
)
|
||||
} else if (module.prototype instanceof BaseService) {
|
||||
theseServiceClasses.push(module)
|
||||
} else if (module.constructor === Array || module.constructor === Object) {
|
||||
for (const key in module) {
|
||||
const serviceClass = module[key]
|
||||
if (serviceClass.prototype instanceof BaseService) {
|
||||
theseServiceClasses.push(serviceClass)
|
||||
} else {
|
||||
throw new InvalidService(
|
||||
`Expected ${servicePath} to export a service or a collection of services; one of them was ${serviceClass}`
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new InvalidService(
|
||||
`Expected ${servicePath} to export a service or a collection of services; got ${module}`
|
||||
)
|
||||
}
|
||||
|
||||
// Decorate each service class with the directory that contains it.
|
||||
theseServiceClasses.forEach(serviceClass => {
|
||||
serviceClass.serviceFamily = servicePath
|
||||
.replace(serviceDir, '')
|
||||
.split(path.sep)[1]
|
||||
currentServiceClasses.forEach(serviceClass => {
|
||||
if (serviceClass && serviceClass.prototype instanceof BaseService) {
|
||||
// Decorate each service class with the directory that contains it.
|
||||
serviceClass.serviceFamily = servicePath
|
||||
.replace(serviceDir, '')
|
||||
.split(path.sep)[1]
|
||||
serviceClass.validateDefinition()
|
||||
return serviceClasses.push(serviceClass)
|
||||
}
|
||||
throw new InvalidService(
|
||||
`Expected ${servicePath} to export a service or a collection of services; one of them was ${serviceClass}`
|
||||
)
|
||||
})
|
||||
|
||||
serviceClasses = serviceClasses.concat(theseServiceClasses)
|
||||
})
|
||||
|
||||
serviceClasses.forEach(ServiceClass => ServiceClass.validateDefinition())
|
||||
}
|
||||
|
||||
return serviceClasses
|
||||
}
|
||||
@@ -80,8 +68,8 @@ function assertNamesUnique(names, { message }) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkNames() {
|
||||
const services = loadServiceClasses()
|
||||
async function checkNames() {
|
||||
const services = await loadServiceClasses()
|
||||
assertNamesUnique(
|
||||
services.map(({ name }) => name),
|
||||
{
|
||||
@@ -90,8 +78,8 @@ function checkNames() {
|
||||
)
|
||||
}
|
||||
|
||||
function collectDefinitions() {
|
||||
const services = loadServiceClasses()
|
||||
async function collectDefinitions() {
|
||||
const services = (await loadServiceClasses())
|
||||
// flatMap.
|
||||
.map(ServiceClass => ServiceClass.getDefinition())
|
||||
.reduce((accum, these) => accum.concat(these), [])
|
||||
@@ -103,13 +91,15 @@ function collectDefinitions() {
|
||||
return result
|
||||
}
|
||||
|
||||
function loadTesters() {
|
||||
return glob
|
||||
.sync(path.join(serviceDir, '**', '*.tester.js'))
|
||||
.map(path => require(path))
|
||||
async function loadTesters() {
|
||||
return Promise.all(
|
||||
glob
|
||||
.sync(path.join(serviceDir, '**', '*.tester.js'))
|
||||
.map(async path => await import(`file://${path}`))
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
InvalidService,
|
||||
loadServiceClasses,
|
||||
checkNames,
|
||||
|
||||
@@ -1,59 +1,67 @@
|
||||
'use strict'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import chai from 'chai'
|
||||
import chaiAsPromised from 'chai-as-promised'
|
||||
import { loadServiceClasses, InvalidService } from './loader.js'
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { loadServiceClasses, InvalidService } = require('./loader')
|
||||
const { expect } = chai
|
||||
const fixturesDir = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'loader-test-fixtures'
|
||||
)
|
||||
|
||||
describe('loadServiceClasses function', function () {
|
||||
it('throws if module exports empty', function () {
|
||||
expect(() =>
|
||||
loadServiceClasses(['./loader-test-fixtures/empty-undefined.fixture.js'])
|
||||
).to.throw(InvalidService)
|
||||
expect(() =>
|
||||
loadServiceClasses(['./loader-test-fixtures/empty-array.fixture.js'])
|
||||
).to.throw()
|
||||
expect(() =>
|
||||
loadServiceClasses(['./loader-test-fixtures/empty-object.fixture.js'])
|
||||
).to.throw(InvalidService)
|
||||
expect(() =>
|
||||
loadServiceClasses(['./loader-test-fixtures/empty-no-export.fixture.js'])
|
||||
).to.throw(InvalidService)
|
||||
expect(() =>
|
||||
it('throws if module exports empty', async function () {
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-undefined.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-array.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-object.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-no-export.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
'./loader-test-fixtures/valid-array.fixture.js',
|
||||
'./loader-test-fixtures/valid-class.fixture.js',
|
||||
'./loader-test-fixtures/empty-array.fixture.js',
|
||||
path.join(fixturesDir, 'valid-array.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-class.fixture.js'),
|
||||
path.join(fixturesDir, 'empty-array.fixture.js'),
|
||||
])
|
||||
).to.throw(InvalidService)
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
})
|
||||
|
||||
it('throws if module exports invalid', function () {
|
||||
expect(() =>
|
||||
loadServiceClasses(['./loader-test-fixtures/invalid-no-base.fixture.js'])
|
||||
).to.throw(InvalidService)
|
||||
expect(() =>
|
||||
it('throws if module exports invalid', async function () {
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'invalid-no-base.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
'./loader-test-fixtures/invalid-wrong-base.fixture.js',
|
||||
path.join(fixturesDir, 'invalid-wrong-base.fixture.js'),
|
||||
])
|
||||
).to.throw(InvalidService)
|
||||
expect(() =>
|
||||
loadServiceClasses(['./loader-test-fixtures/invalid-mixed.fixture.js'])
|
||||
).to.throw(InvalidService)
|
||||
expect(() =>
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'invalid-mixed.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
'./loader-test-fixtures/valid-array.fixture.js',
|
||||
'./loader-test-fixtures/valid-class.fixture.js',
|
||||
'./loader-test-fixtures/invalid-no-base.fixture.js',
|
||||
path.join(fixturesDir, 'valid-array.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-class.fixture.js'),
|
||||
path.join(fixturesDir, 'invalid-no-base.fixture.js'),
|
||||
])
|
||||
).to.throw(InvalidService)
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
})
|
||||
|
||||
it('registers services if module exports valid service classes', function () {
|
||||
expect(
|
||||
it('registers services if module exports valid service classes', async function () {
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
'./loader-test-fixtures/valid-array.fixture.js',
|
||||
'./loader-test-fixtures/valid-object.fixture.js',
|
||||
'./loader-test-fixtures/valid-class.fixture.js',
|
||||
path.join(fixturesDir, 'valid-array.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-object.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-class.fixture.js'),
|
||||
])
|
||||
).to.have.length(5)
|
||||
).to.eventually.have.length(5)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
const { performance } = require('perf_hooks')
|
||||
import { performance } from 'perf_hooks'
|
||||
|
||||
class MetricHelper {
|
||||
constructor({ metricInstance }, { category, serviceFamily, name }) {
|
||||
@@ -59,4 +57,4 @@ const MetricNames = Object.freeze({
|
||||
SERVICE_RESPONSE_SIZE: Symbol('service-response-size'),
|
||||
})
|
||||
|
||||
module.exports = { MetricHelper, MetricNames }
|
||||
export { MetricHelper, MetricNames }
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const camelcase = require('camelcase')
|
||||
const emojic = require('emojic')
|
||||
const Joi = require('joi')
|
||||
const queryString = require('query-string')
|
||||
const BaseService = require('./base')
|
||||
const {
|
||||
import camelcase from 'camelcase'
|
||||
import emojic from 'emojic'
|
||||
import Joi from 'joi'
|
||||
import queryString from 'query-string'
|
||||
import BaseService from './base.js'
|
||||
import {
|
||||
serverHasBeenUpSinceResourceCached,
|
||||
setCacheHeadersForStaticResource,
|
||||
} = require('./cache-headers')
|
||||
const { isValidCategory } = require('./categories')
|
||||
const { MetricHelper } = require('./metric-helper')
|
||||
const { isValidRoute, prepareRoute, namedParamsForMatch } = require('./route')
|
||||
const trace = require('./trace')
|
||||
} from './cache-headers.js'
|
||||
import { isValidCategory } from './categories.js'
|
||||
import { MetricHelper } from './metric-helper.js'
|
||||
import { isValidRoute, prepareRoute, namedParamsForMatch } from './route.js'
|
||||
import trace from './trace.js'
|
||||
|
||||
const attrSchema = Joi.object({
|
||||
name: Joi.string().min(3),
|
||||
category: isValidCategory,
|
||||
isDeprecated: Joi.boolean().default(true),
|
||||
route: isValidRoute,
|
||||
examples: Joi.array().has(Joi.object()).default([]),
|
||||
transformPath: Joi.func()
|
||||
.maxArity(1)
|
||||
.required()
|
||||
@@ -30,11 +30,13 @@ const attrSchema = Joi.object({
|
||||
overrideTransformedQueryParams: Joi.bool().optional(),
|
||||
}).required()
|
||||
|
||||
module.exports = function redirector(attrs) {
|
||||
export default function redirector(attrs) {
|
||||
const {
|
||||
name,
|
||||
category,
|
||||
isDeprecated,
|
||||
route,
|
||||
examples,
|
||||
transformPath,
|
||||
transformQueryParams,
|
||||
overrideTransformedQueryParams,
|
||||
@@ -48,8 +50,9 @@ module.exports = function redirector(attrs) {
|
||||
})}Redirect`
|
||||
|
||||
static category = category
|
||||
static isDeprecated = true
|
||||
static isDeprecated = isDeprecated
|
||||
static route = route
|
||||
static examples = examples
|
||||
|
||||
static register({ camp, metricInstance }, { rasterUrl }) {
|
||||
const { regex, captureNames } = prepareRoute({
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const Camp = require('@shields_io/camp')
|
||||
const portfinder = require('portfinder')
|
||||
const { expect } = require('chai')
|
||||
const got = require('../got-test-client')
|
||||
const redirector = require('./redirector')
|
||||
import Camp from '@shields_io/camp'
|
||||
import portfinder from 'portfinder'
|
||||
import { expect } from 'chai'
|
||||
import got from '../got-test-client.js'
|
||||
import redirector from './redirector.js'
|
||||
|
||||
describe('Redirector', function () {
|
||||
const route = {
|
||||
@@ -47,6 +45,24 @@ describe('Redirector', function () {
|
||||
).to.throw('"dateAdded" is required')
|
||||
})
|
||||
|
||||
it('sets specified example', function () {
|
||||
const examples = [
|
||||
{
|
||||
title: 'very old service',
|
||||
pattern: ':namedParamA',
|
||||
namedParams: {
|
||||
namedParamA: 'namedParamAValue',
|
||||
},
|
||||
staticPreview: {
|
||||
label: 'service',
|
||||
message: 'v0.14.0',
|
||||
color: 'blue',
|
||||
},
|
||||
},
|
||||
]
|
||||
expect(redirector({ ...attrs, examples }).examples).to.equal(examples)
|
||||
})
|
||||
|
||||
describe('ScoutCamp integration', function () {
|
||||
let port, baseUrl
|
||||
beforeEach(async function () {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const escapeStringRegexp = require('escape-string-regexp')
|
||||
const Joi = require('joi')
|
||||
const { pathToRegexp } = require('path-to-regexp')
|
||||
import escapeStringRegexp from 'escape-string-regexp'
|
||||
import Joi from 'joi'
|
||||
import { pathToRegexp } from 'path-to-regexp'
|
||||
|
||||
function makeFullUrl(base, partialUrl) {
|
||||
return `/${[base, partialUrl].filter(Boolean).join('/')}`
|
||||
@@ -74,7 +72,7 @@ function getQueryParamNames({ queryParamSchema }) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
makeFullUrl,
|
||||
isValidRoute,
|
||||
assertValidRoute,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const Joi = require('joi')
|
||||
const { test, given, forCases } = require('sazerac')
|
||||
const {
|
||||
import { expect } from 'chai'
|
||||
import Joi from 'joi'
|
||||
import { test, given, forCases } from 'sazerac'
|
||||
import {
|
||||
prepareRoute,
|
||||
namedParamsForMatch,
|
||||
getQueryParamNames,
|
||||
} = require('./route')
|
||||
} from './route.js'
|
||||
|
||||
describe('Route helpers', function () {
|
||||
context('A `pattern` with a named param is declared', function () {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
import Joi from 'joi'
|
||||
|
||||
// This should be kept in sync with the schema in
|
||||
// `frontend/lib/service-definitions/index.ts`.
|
||||
@@ -72,7 +70,7 @@ function assertValidServiceDefinitionExport(examples, message = undefined) {
|
||||
Joi.assert(examples, serviceDefinitionExport, message)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
serviceDefinition,
|
||||
assertValidServiceDefinition,
|
||||
serviceDefinitionExport,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function toArray(val) {
|
||||
export default function toArray(val) {
|
||||
if (val === undefined) {
|
||||
return []
|
||||
} else if (Object(val) instanceof Array) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const chalk = require('chalk')
|
||||
const config = require('config').util.toObject()
|
||||
import chalk from 'chalk'
|
||||
import config from 'config'
|
||||
const objectConfig = config.util.toObject()
|
||||
|
||||
// Config is loaded globally but it would be better to inject it. To do that,
|
||||
// there needs to be one instance of the service created at registration time,
|
||||
@@ -10,7 +9,7 @@ const config = require('config').util.toObject()
|
||||
// thereby gaining access to the injected config.
|
||||
const {
|
||||
services: { trace: enableTraceLogging },
|
||||
} = config.public
|
||||
} = objectConfig.public
|
||||
|
||||
function _formatLabelForStage(stage, label) {
|
||||
const colorFn = {
|
||||
@@ -37,6 +36,6 @@ function logTrace(stage, symbol, label, content, { deep = false } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
logTrace,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const emojic = require('emojic')
|
||||
const Joi = require('joi')
|
||||
const trace = require('./trace')
|
||||
import emojic from 'emojic'
|
||||
import Joi from 'joi'
|
||||
import trace from './trace.js'
|
||||
|
||||
function validate(
|
||||
{
|
||||
@@ -50,4 +48,4 @@ function validate(
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = validate
|
||||
export default validate
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const trace = require('./trace')
|
||||
const { InvalidParameter } = require('./errors')
|
||||
const validate = require('./validate')
|
||||
import Joi from 'joi'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import trace from './trace.js'
|
||||
import { InvalidParameter } from './errors.js'
|
||||
import validate from './validate.js'
|
||||
|
||||
describe('validate', function () {
|
||||
const schema = Joi.object({
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict'
|
||||
|
||||
const got = require('got')
|
||||
import got from 'got'
|
||||
|
||||
// https://github.com/nock/nock/issues/1523
|
||||
module.exports = got.extend({ retry: 0 })
|
||||
export default got.extend({ retry: 0 })
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user