Skip to content

Commit cf736fe

Browse files
committed
handle errors in supporter images during 11ty build
- `canvas` now checks for errors and discards supporters, which will avoid problems during postbuild step - output debug info in build - remove `image-size` in lieu of `canvas` which is now a direct dev dependency
1 parent f966c94 commit cf736fe

File tree

4 files changed

+63
-51
lines changed

4 files changed

+63
-51
lines changed

docs/_data/supporters.js

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717

1818
'use strict';
1919

20-
const {writeFile, mkdir} = require('fs').promises;
20+
const {loadImage} = require('canvas');
21+
const {writeFile, mkdir, rmdir} = require('fs').promises;
2122
const {resolve} = require('path');
2223
const debug = require('debug')('mocha:docs:data:supporters');
2324
const needle = require('needle');
24-
const imageSize = require('image-size');
2525
const blocklist = new Set(require('./blocklist.json'));
2626

2727
/**
@@ -36,7 +36,12 @@ const BLOCKED_STRINGS = /(?:vpn|[ck]a[sz]ino|seo|slots|gambl(?:e|ing)|crypto)/i;
3636
*/
3737
const API_ENDPOINT = 'https://api.opencollective.com/graphql/v2';
3838

39-
const query = `query account($limit: Int, $offset: Int, $slug: String) {
39+
const SPONSOR_TIER = 'sponsor';
40+
const BACKER_TIER = 'backer';
41+
42+
const SUPPORTER_IMAGE_PATH = resolve(__dirname, '../images/supporters');
43+
44+
const SUPPORTER_QUERY = `query account($limit: Int, $offset: Int, $slug: String) {
4045
account(slug: $slug) {
4146
orders(limit: $limit, offset: $offset) {
4247
limit
@@ -61,7 +66,9 @@ const query = `query account($limit: Int, $offset: Int, $slug: String) {
6166
}
6267
}`;
6368

64-
const graphqlPageSize = 1000;
69+
const GRAPHQL_PAGE_SIZE = 1000;
70+
71+
const invalidSupporters = [];
6572

6673
const nodeToSupporter = node => ({
6774
id: node.fromAccount.id,
@@ -75,6 +82,30 @@ const nodeToSupporter = node => ({
7582
type: node.fromAccount.type
7683
});
7784

85+
const fetchImage = async supporter => {
86+
try {
87+
const {avatar: url} = supporter;
88+
const {body: imageBuf} = await needle('get', url);
89+
debug('fetched %s', url);
90+
const canvasImage = await loadImage(imageBuf);
91+
debug('ok %s', url);
92+
supporter.dimensions = {
93+
width: canvasImage.width,
94+
height: canvasImage.height
95+
};
96+
debug('dimensions %s %dw %dh', url, canvasImage.width, canvasImage.height);
97+
const filePath = resolve(SUPPORTER_IMAGE_PATH, supporter.id + '.png');
98+
await writeFile(filePath, imageBuf);
99+
debug('wrote %s', filePath);
100+
} catch (err) {
101+
console.error(
102+
`failed to load ${supporter.avatar}; will discard ${supporter.tier} "${supporter.name} (${supporter.slug}). reason:\n`,
103+
err
104+
);
105+
invalidSupporters.push(supporter);
106+
}
107+
};
108+
78109
/**
79110
* Retrieves donation data from OC
80111
*
@@ -84,26 +115,26 @@ const nodeToSupporter = node => ({
84115
*/
85116
const getAllOrders = async (slug = 'mochajs') => {
86117
let allOrders = [];
87-
const variables = {limit: graphqlPageSize, offset: 0, slug};
118+
const variables = {limit: GRAPHQL_PAGE_SIZE, offset: 0, slug};
88119

89120
// Handling pagination if necessary (2 pages for ~1400 results in May 2019)
90121
while (true) {
91122
const result = await needle(
92123
'post',
93124
API_ENDPOINT,
94-
{query, variables},
125+
{query: SUPPORTER_QUERY, variables},
95126
{json: true}
96127
);
97128
const orders = result.body.data.account.orders.nodes;
98129
allOrders = [...allOrders, ...orders];
99-
variables.offset += graphqlPageSize;
100-
if (orders.length < graphqlPageSize) {
130+
variables.offset += GRAPHQL_PAGE_SIZE;
131+
if (orders.length < GRAPHQL_PAGE_SIZE) {
101132
debug('retrieved %d orders', allOrders.length);
102133
return allOrders;
103134
} else {
104135
debug(
105136
'loading page %d of orders...',
106-
Math.floor(variables.offset / graphqlPageSize)
137+
Math.floor(variables.offset / GRAPHQL_PAGE_SIZE)
107138
);
108139
}
109140
}
@@ -143,7 +174,8 @@ const getSupporters = async () => {
143174
...supporters.backers,
144175
{
145176
...supporter,
146-
avatar: encodeURI(supporter.imgUrlSmall)
177+
avatar: encodeURI(supporter.imgUrlSmall),
178+
tier: BACKER_TIER
147179
}
148180
];
149181
}
@@ -152,7 +184,8 @@ const getSupporters = async () => {
152184
...supporters.sponsors,
153185
{
154186
...supporter,
155-
avatar: encodeURI(supporter.imgUrlMed)
187+
avatar: encodeURI(supporter.imgUrlMed),
188+
tier: SPONSOR_TIER
156189
}
157190
];
158191
}
@@ -164,38 +197,34 @@ const getSupporters = async () => {
164197
}
165198
);
166199

167-
const supporterImagePath = resolve(__dirname, '../images/supporters');
168-
169-
await mkdir(supporterImagePath, {recursive: true});
200+
await rmdir(SUPPORTER_IMAGE_PATH, {recursive: true});
201+
debug('blasted %s', SUPPORTER_IMAGE_PATH);
202+
await mkdir(SUPPORTER_IMAGE_PATH, {recursive: true});
203+
debug('created %s', SUPPORTER_IMAGE_PATH);
170204

171205
// Fetch images for sponsors and save their image dimensions
172-
await Promise.all(
173-
supporters.sponsors.map(async sponsor => {
174-
const filePath = resolve(supporterImagePath, `${sponsor.id}.png`);
175-
const {body} = await needle('get', sponsor.avatar);
176-
sponsor.dimensions = imageSize(body);
177-
await writeFile(filePath, body);
178-
})
179-
);
180-
181-
// Fetch images for backers and save their image dimensions
182-
await Promise.all(
183-
supporters.backers.map(async backer => {
184-
const filePath = resolve(supporterImagePath, `${backer.id}.png`);
185-
const {body} = await needle('get', backer.avatar);
186-
await writeFile(filePath, body);
187-
})
188-
);
206+
await Promise.all([
207+
...supporters.sponsors.map(fetchImage),
208+
...supporters.backers.map(fetchImage)
209+
]);
210+
211+
invalidSupporters.forEach(supporter => {
212+
supporters[supporter.tier].splice(
213+
supporters[supporter.tier].indexOf(supporter),
214+
1
215+
);
216+
});
189217

190218
const backerCount = supporters.backers.length;
191219
const sponsorCount = supporters.sponsors.length;
192220
const totalValidSupportersCount = backerCount + sponsorCount;
193221

194222
debug(
195-
'found %d valid backers and %d valid sponsors (of %d total; %d blocked)',
223+
'found %d valid backers and %d valid sponsors (%d total; %d invalid; %d blocked)',
196224
backerCount,
197225
sponsorCount,
198226
totalValidSupportersCount,
227+
invalidSupporters.length,
199228
uniqueSupporters.size - totalValidSupportersCount
200229
);
201230
return supporters;

netlify.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
command = "npm start docs.production"
1414

1515
[build.environment]
16+
DEBUG = "mocha:docs*"
1617
NODE_VERSION = "12"
1718
RUBY_VERSION = "2.7.1"
1819

package-lock.json

Lines changed: 0 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"assetgraph-builder": "^8.0.1",
9191
"autoprefixer": "^9.7.4",
9292
"babel-eslint": "^10.1.0",
93+
"canvas": "^2.6.1",
9394
"chai": "^4.2.0",
9495
"coffee-script": "^1.12.7",
9596
"core-js": "^3.6.5",
@@ -108,7 +109,6 @@
108109
"fs-extra": "^9.0.0",
109110
"husky": "^4.2.3",
110111
"hyperlink": "^4.4.3",
111-
"image-size": "^0.8.3",
112112
"jsdoc": "^3.6.3",
113113
"karma": "^4.4.1",
114114
"karma-chrome-launcher": "^3.1.0",

0 commit comments

Comments
 (0)