├── .gitignore
├── README.md
├── index.js
├── package.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Give thanks (in the form of a [GitHub ★ ](https://help.github.com/articles/about-stars/)) to your fellow JS package maintainers!
6 |
7 |
8 |
9 |
10 | ## Usage
11 |
12 | ```sh
13 | $ npx sharelove
14 | ```
15 |
16 | This will find all of your NPM dependencies, find their github.com repository, and star their GitHub repositories. This was inspired by [`symfony/thanks`](https://github.com/symfony/thanks).
17 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const got = require('got')
4 | const { join } = require('path')
5 | const colors = require('colors')
6 | const emoji = require('node-emoji')
7 | const { dependencies, devDependencies } = require(join(process.cwd(), 'package.json'))
8 |
9 | const aliases = {}
10 | const notStarred = {}
11 | const cannotStar = []
12 | const allDependencies = Object.keys({ ...dependencies, ...devDependencies })
13 |
14 | if (!process.env.GITHUB_API_TOKEN) {
15 | console.log(colors.red(`${emoji.get('warning')} We use GITHUB_API_TOKEN environment variable to call the Github API.`))
16 | console.log(colors.red(`Please run ${colors.bold('export GITHUB_API_TOKEN="YOUR_TOKEN"')} and try again.`))
17 | process.exit()
18 | }
19 |
20 | async function callGithubApi (query) {
21 | const { body } = await got.post('https://api.github.com/graphql', {
22 | body: { query },
23 | headers: { authorization: `Bearer ${process.env.GITHUB_API_TOKEN}` },
24 | json: true,
25 | })
26 |
27 | return body.data
28 | }
29 |
30 | async function fetchPackagesInformation () {
31 | const { body } = await got.post('https://api.npms.io/v2/package/mget', {
32 | body: allDependencies,
33 | json: true,
34 | })
35 |
36 | return Object.entries(body).reduce((pkgs, [pkgName, pkgInfo]) => {
37 | try {
38 | const url = pkgInfo.collected.metadata.repository.url.split('/')
39 | const owner = url[3]
40 | const repository = url[4].substring(0, url[4].lastIndexOf('.'))
41 |
42 | pkgs.push({ name: pkgName, owner, repository })
43 | } catch (e) {
44 | cannotStar.push(pkgName)
45 | }
46 |
47 | return pkgs
48 | }, [])
49 | }
50 |
51 | async function thanksDependencies () {
52 | const pkgs = await fetchPackagesInformation()
53 | let query = ''
54 |
55 | pkgs.forEach(({ name, owner, repository }, index) => {
56 | query += `_${index}: repository(owner:"${owner}",name:"${repository}"){id,viewerHasStarred}\n`
57 | aliases[`_${index}`] = { name, owner, repository }
58 | })
59 |
60 | const repos = await callGithubApi(`query{${query}}`)
61 | query = ''
62 |
63 | Object.entries(repos).forEach(([alias, repo], index) => {
64 | if (!repo) {
65 | cannotStar.push(aliases[alias].name)
66 | return
67 | }
68 |
69 | if (!repo.viewerHasStarred) {
70 | query += `_${index}: addStar(input:{clientMutationId:"${alias}",starrableId:"${repo.id}"}){clientMutationId}\n`
71 | notStarred[alias] = repo
72 | }
73 | })
74 |
75 | if (Object.keys(notStarred).length <= 0) {
76 | console.log(`You already starred all your GitHub dependencies. ${emoji.get('heart')}\n`)
77 | } else {
78 | await callGithubApi(`mutation{${query}}`)
79 |
80 | console.log(`Stars sent to :`)
81 |
82 | Object.entries(notStarred).forEach(([alias, repo]) => {
83 | console.log(` - ${emoji.get('star')} ${colors.blue(aliases[alias].name)}`)
84 | })
85 | }
86 |
87 | cannotStar.forEach((name) => {
88 | console.log(` - Cannot ${emoji.get('star')} ${colors.blue(name)} ${emoji.get('cry')}`)
89 | })
90 |
91 | console.log(`\nThanks to you! ${emoji.get('heart')}`)
92 |
93 | process.exit()
94 | }
95 |
96 | thanksDependencies()
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sharelove",
3 | "version": "1.0.4",
4 | "description": "Give thanks (in the form of a GitHub ★) to your fellow JS package maintainers.",
5 | "keywords": [
6 | "thanks",
7 | "stars",
8 | "github",
9 | "dependencies"
10 | ],
11 | "author": "Romain Lanz ",
12 | "license": "MIT",
13 | "main": "index.js",
14 | "bin": "index.js",
15 | "dependencies": {
16 | "colors": "^1.1.2",
17 | "got": "^8.2.0",
18 | "node-emoji": "^1.8.1"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/Slynova-Org/sharelove"
23 | },
24 | "bugs": {
25 | "url": "https://github.com/Slynova-Org/sharelove/issues"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@sindresorhus/is@^0.7.0":
6 | version "0.7.0"
7 | resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
8 |
9 | cacheable-request@^2.1.1:
10 | version "2.1.4"
11 | resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d"
12 | dependencies:
13 | clone-response "1.0.2"
14 | get-stream "3.0.0"
15 | http-cache-semantics "3.8.1"
16 | keyv "3.0.0"
17 | lowercase-keys "1.0.0"
18 | normalize-url "2.0.1"
19 | responselike "1.0.2"
20 |
21 | clone-response@1.0.2:
22 | version "1.0.2"
23 | resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
24 | dependencies:
25 | mimic-response "^1.0.0"
26 |
27 | colors@^1.1.2:
28 | version "1.1.2"
29 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
30 |
31 | core-util-is@~1.0.0:
32 | version "1.0.2"
33 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
34 |
35 | decode-uri-component@^0.2.0:
36 | version "0.2.0"
37 | resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
38 |
39 | decompress-response@^3.3.0:
40 | version "3.3.0"
41 | resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
42 | dependencies:
43 | mimic-response "^1.0.0"
44 |
45 | duplexer3@^0.1.4:
46 | version "0.1.4"
47 | resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
48 |
49 | from2@^2.1.1:
50 | version "2.3.0"
51 | resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
52 | dependencies:
53 | inherits "^2.0.1"
54 | readable-stream "^2.0.0"
55 |
56 | get-stream@3.0.0, get-stream@^3.0.0:
57 | version "3.0.0"
58 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
59 |
60 | got@^8.2.0:
61 | version "8.2.0"
62 | resolved "https://registry.yarnpkg.com/got/-/got-8.2.0.tgz#0d11a071d05046348a2f5c0a5fa047fb687fdfc6"
63 | dependencies:
64 | "@sindresorhus/is" "^0.7.0"
65 | cacheable-request "^2.1.1"
66 | decompress-response "^3.3.0"
67 | duplexer3 "^0.1.4"
68 | get-stream "^3.0.0"
69 | into-stream "^3.1.0"
70 | is-retry-allowed "^1.1.0"
71 | isurl "^1.0.0-alpha5"
72 | lowercase-keys "^1.0.0"
73 | mimic-response "^1.0.0"
74 | p-cancelable "^0.3.0"
75 | p-timeout "^2.0.1"
76 | pify "^3.0.0"
77 | safe-buffer "^5.1.1"
78 | timed-out "^4.0.1"
79 | url-parse-lax "^3.0.0"
80 | url-to-options "^1.0.1"
81 |
82 | has-symbol-support-x@^1.4.1:
83 | version "1.4.1"
84 | resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz#66ec2e377e0c7d7ccedb07a3a84d77510ff1bc4c"
85 |
86 | has-to-string-tag-x@^1.2.0:
87 | version "1.4.1"
88 | resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
89 | dependencies:
90 | has-symbol-support-x "^1.4.1"
91 |
92 | http-cache-semantics@3.8.1:
93 | version "3.8.1"
94 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
95 |
96 | inherits@^2.0.1, inherits@~2.0.3:
97 | version "2.0.3"
98 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
99 |
100 | into-stream@^3.1.0:
101 | version "3.1.0"
102 | resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6"
103 | dependencies:
104 | from2 "^2.1.1"
105 | p-is-promise "^1.1.0"
106 |
107 | is-object@^1.0.1:
108 | version "1.0.1"
109 | resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
110 |
111 | is-plain-obj@^1.0.0:
112 | version "1.1.0"
113 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
114 |
115 | is-retry-allowed@^1.1.0:
116 | version "1.1.0"
117 | resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
118 |
119 | isarray@~1.0.0:
120 | version "1.0.0"
121 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
122 |
123 | isurl@^1.0.0-alpha5:
124 | version "1.0.0"
125 | resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
126 | dependencies:
127 | has-to-string-tag-x "^1.2.0"
128 | is-object "^1.0.1"
129 |
130 | json-buffer@3.0.0:
131 | version "3.0.0"
132 | resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
133 |
134 | keyv@3.0.0:
135 | version "3.0.0"
136 | resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373"
137 | dependencies:
138 | json-buffer "3.0.0"
139 |
140 | lodash.toarray@^4.4.0:
141 | version "4.4.0"
142 | resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
143 |
144 | lowercase-keys@1.0.0, lowercase-keys@^1.0.0:
145 | version "1.0.0"
146 | resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
147 |
148 | mimic-response@^1.0.0:
149 | version "1.0.0"
150 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e"
151 |
152 | node-emoji@^1.8.1:
153 | version "1.8.1"
154 | resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.8.1.tgz#6eec6bfb07421e2148c75c6bba72421f8530a826"
155 | dependencies:
156 | lodash.toarray "^4.4.0"
157 |
158 | normalize-url@2.0.1:
159 | version "2.0.1"
160 | resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
161 | dependencies:
162 | prepend-http "^2.0.0"
163 | query-string "^5.0.1"
164 | sort-keys "^2.0.0"
165 |
166 | object-assign@^4.1.0:
167 | version "4.1.1"
168 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
169 |
170 | p-cancelable@^0.3.0:
171 | version "0.3.0"
172 | resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
173 |
174 | p-finally@^1.0.0:
175 | version "1.0.0"
176 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
177 |
178 | p-is-promise@^1.1.0:
179 | version "1.1.0"
180 | resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
181 |
182 | p-timeout@^2.0.1:
183 | version "2.0.1"
184 | resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038"
185 | dependencies:
186 | p-finally "^1.0.0"
187 |
188 | pify@^3.0.0:
189 | version "3.0.0"
190 | resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
191 |
192 | prepend-http@^2.0.0:
193 | version "2.0.0"
194 | resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
195 |
196 | process-nextick-args@~2.0.0:
197 | version "2.0.0"
198 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
199 |
200 | query-string@^5.0.1:
201 | version "5.1.0"
202 | resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.0.tgz#9583b15fd1307f899e973ed418886426a9976469"
203 | dependencies:
204 | decode-uri-component "^0.2.0"
205 | object-assign "^4.1.0"
206 | strict-uri-encode "^1.0.0"
207 |
208 | readable-stream@^2.0.0:
209 | version "2.3.4"
210 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
211 | dependencies:
212 | core-util-is "~1.0.0"
213 | inherits "~2.0.3"
214 | isarray "~1.0.0"
215 | process-nextick-args "~2.0.0"
216 | safe-buffer "~5.1.1"
217 | string_decoder "~1.0.3"
218 | util-deprecate "~1.0.1"
219 |
220 | responselike@1.0.2:
221 | version "1.0.2"
222 | resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
223 | dependencies:
224 | lowercase-keys "^1.0.0"
225 |
226 | safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
227 | version "5.1.1"
228 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
229 |
230 | sort-keys@^2.0.0:
231 | version "2.0.0"
232 | resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
233 | dependencies:
234 | is-plain-obj "^1.0.0"
235 |
236 | strict-uri-encode@^1.0.0:
237 | version "1.1.0"
238 | resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
239 |
240 | string_decoder@~1.0.3:
241 | version "1.0.3"
242 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
243 | dependencies:
244 | safe-buffer "~5.1.0"
245 |
246 | timed-out@^4.0.1:
247 | version "4.0.1"
248 | resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
249 |
250 | url-parse-lax@^3.0.0:
251 | version "3.0.0"
252 | resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
253 | dependencies:
254 | prepend-http "^2.0.0"
255 |
256 | url-to-options@^1.0.1:
257 | version "1.0.1"
258 | resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
259 |
260 | util-deprecate@~1.0.1:
261 | version "1.0.2"
262 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
263 |
--------------------------------------------------------------------------------