Skip to content

Commit 40ad1ab

Browse files
committed
✨ Display GitHub stars count on each workshop
1 parent 9512999 commit 40ad1ab

File tree

7 files changed

+99
-13
lines changed

7 files changed

+99
-13
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Generate your GitHub Personnal Access Token here: https://github.com/settings/tokens
2+
GITHUB_PERSONAL_ACCESS_TOKEN=put_your_github_token_here

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
.env
23
.idea
34
.build*
45
_site

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ We ask that you make pull requests because changes to this repository will get d
1212

1313
## Running the entire app locally
1414

15-
This is a static site with a build step, simply run `npm start`:
15+
This is a static site with a build step. The build step uses a [GitHub Personnal Access Token](https://github.com/settings/tokens) that you need to generate and put in a .env file. **Actually you don't need to configure any authorization on this access token**, it just serves the purpose of unleashing the GitHub API Rate Limit.
16+
17+
```
18+
cp .env.example .env
19+
// then put your GitHub Personnal Access Token in the .env file
20+
```
21+
After that, simply run `npm start`:
1622

1723
```
1824
npm install
@@ -25,10 +31,10 @@ Please refer to the [nodeschool site](http://nodeschool.io/#workshoppers) for de
2531

2632
- **Globally** (easiest)
2733

28-
Depending on the npm version, `npm packages` get installed in different routes. To have access to them globally. Do
29-
34+
Depending on the npm version, `npm packages` get installed in different routes. To have access to them globally. Do
35+
3036
`npm install -global package_name` or `npm install -g package_name`
31-
37+
3238
If you get a `permission denied` **error**. Run the previous command with `sudo`.
3339

3440
`sudo npm install -g package_name`
@@ -52,10 +58,10 @@ If you would like to keep all the node_school workshop packages inside a custom
5258
```
5359
~ mkdir -p node_school
5460
cd node_school
55-
npm install javascripting
61+
npm install javascripting
5662
```
5763

58-
From within the `node_school` directory now run `node_modules/learnyounode/bin/javascripting` to start it.
64+
From within the `node_school` directory now run `node_modules/learnyounode/bin/javascripting` to start it.
5965

6066
This is because you need to run the executable from within the directory itself since it's not available globally in your `$PATH`
6167

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@
3434
"devDependencies": {
3535
"after-all": "^2.0.1",
3636
"autoprefixer-stylus": "^0.9.4",
37+
"axios": "^0.18.0",
3738
"browserify": "^12.0.1",
3839
"cheerio": "^0.19.0",
40+
"dotenv": "^6.1.0",
3941
"gaze": "^0.5.1",
4042
"glob": "^6.0.3",
4143
"jsdom": "^7.1.0",

scripts/build-html.js

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,25 @@ const Path = require('path')
44
const Fs = require('fs')
55
const jsdom = require('jsdom')
66
const mkdirp = require('mkdirp')
7+
const { performance } = require('perf_hooks');
78
const cmdwatcher = require('./util/cmdwatcher')
89
const LANGS = Path.join(__dirname, '../languages/languages.json')
910
const FOOTER = Path.join(__dirname, '../footer.html')
1011

12+
const env = require('dotenv').config();
13+
if (env.error) {
14+
throw env.error;
15+
}
16+
17+
if (!process.env.GITHUB_PERSONAL_ACCESS_TOKEN) {
18+
throw new Error('GITHUB_PERSONAL_ACCESS_TOKEN environment variable is not found');
19+
}
20+
21+
const axios = require('axios').create({
22+
baseURL: 'https://api.github.com/',
23+
headers: { 'Authorization': `token ${process.env.GITHUB_PERSONAL_ACCESS_TOKEN}` }
24+
});
25+
1126
function createLangButton(dom, file, lang, langName) {
1227
var li = dom.createElement('li')
1328
li.className = 'nav-lang-' + lang
@@ -71,6 +86,50 @@ function getNodeByLanguage(nodes, lang) {
7186
}
7287
}
7388

89+
async function getStargazers(workshopElement) {
90+
const url = require('url');
91+
const githubUrl = workshopElement.getAttribute("href");
92+
let response, parsedUrl;
93+
94+
if (!githubUrl) {
95+
return 0;
96+
}
97+
98+
try {
99+
parsedUrl = url.parse(githubUrl);
100+
} catch (e) {
101+
throw e;
102+
}
103+
104+
try {
105+
response = await axios.get(`/repos${parsedUrl.path}?per_page=1`);
106+
} catch (e) {
107+
throw e;
108+
}
109+
writeStargazers(workshopElement, response.data.stargazers_count)
110+
}
111+
112+
function writeStargazers(workshopElement, stargazersCount) {
113+
const starIconSVG = `<svg style="vertical-align: middle" viewBox="0 0 16 16" version="1.1" width="16" height="16" role="img"><path fill="currentColor" fill-rule="evenodd" d="M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z"></path></svg>`;
114+
workshopElement.innerHTML = `
115+
<span>${workshopElement.innerHTML}</span>
116+
<span>(${starIconSVG}${kFormatter(stargazersCount)})<span>
117+
`;
118+
}
119+
120+
function kFormatter(number) {
121+
return number > 999 ? (number / 1000).toFixed(1) + 'k' : number
122+
}
123+
124+
async function isRateLimitReached(nbToPerform) {
125+
const remaining = await axios.get('/rate_limit')
126+
if (remaining.data.resources.core.remaining < nbToPerform) {
127+
console.error(`💔 GitHub Rate Limit Reached. Reset at ${new Date(remaining.data.resources.core.reset * 1000).toLocaleTimeString()}`);
128+
return true;
129+
}
130+
return false;
131+
}
132+
74133
cmdwatcher('build-html'
75134
, '!(node_modules).html'
76135
, 'languages/**'
@@ -102,27 +161,36 @@ cmdwatcher('build-html'
102161
}
103162
})
104163
languages['en'] = 'English'
105-
files.forEach(function (file) {
164+
files.forEach(async function (file) {
106165
var raw
107166
, dom
108-
, original
167+
, original
109168
try {
110169
raw = Fs.readFileSync(file, 'utf8')
111170
} catch(e) {
112171
return console.log('Error while reading %s:\n%s', file, e)
113172
}
114173
try {
115-
dom = jsdom.jsdom(raw)
174+
dom = jsdom.jsdom(raw)
175+
if (file === 'index.html') {
176+
const t0 = performance.now();
177+
const workshops = dom.getElementsByClassName('js-workshop-link');
178+
await isRateLimitReached(workshops.length);
179+
const githubPromises = Array.from(workshops).map(getStargazers);
180+
await Promise.all(githubPromises);
181+
const t1 = performance.now();
182+
console.log(`⭐️ GitHub Stargazers fetched in ${((t1 - t0) / 1000).toFixed(1)}s`)
183+
}
116184
} catch(e) {
117-
return console.log('Error while domify %s:\n%s', file, e)
118-
}
185+
return console.log('Error while domify %s:\n%s', file, e)
186+
}
119187

120188
var footer = dom.querySelector('footer')
121189
if(footer) {
122190
var footerHtml = Fs.readFileSync(FOOTER, 'utf8')
123191
footer.innerHTML = footerHtml
124192
}
125-
193+
126194
var list = dom.querySelectorAll('[data-i18n]')
127195
if (list) {
128196
original = {}

scripts/build.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
#!/bin/bash
2+
if [[ -f ../.env ]]
3+
then
4+
echo 'Missing .env file'
5+
echo 'Please consult README for setup instructions'
6+
exit 1
7+
fi
28
npm run build-chapters -- $@
39
npm run generate-css
410
npm run build-dependencies

styles/style.styl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,8 @@ span.no-break { display:inline-block; }
455455
font-family: Source Code Pro, monospace;
456456
font-weight: 500;
457457
text-decoration: none;
458-
display: block;
458+
display: flex;
459+
justify-content: space-between;
459460
border: none;
460461
}
461462

0 commit comments

Comments
 (0)