5 const { join } = require('path');
7 const { listDir } = require('hexo-fs');
8 const sharp = require('sharp');
10 function difference(setA, setB) {
11 const diff = new Set(setA);
12 for (const elem of setB) {
18 async function validateTheme() {
20 let isValidationPassed = true;
22 const themeData = hexo.locals.get('data').themes;
23 const themes = new Set();
24 for (const theme of themeData) {
25 const name = theme.name.toLocaleLowerCase();
29 const screenshotsPath = join(hexo.source_dir, 'themes/screenshots');
30 let screenshots = await listDir(screenshotsPath);
32 for (const filename of screenshots) {
33 if (!filename.endsWith('.png')) {
34 message.push(`The theme thumbnail "${filename}" is not a png image.`);
35 isValidationPassed = false;
38 const screenshot = join(screenshotsPath, filename);
40 const { width, height } = await sharp(screenshot).metadata();
41 if (width !== 800 || height !== 500) {
43 `The theme thumbnail "${filename}" is not sized in 800x500 (got ${width}x${height}).`
45 isValidationPassed = false;
49 screenshots = new Set(screenshots.map(name => name.replace('.png', '').toLocaleLowerCase()));
51 const diffThemesScreenshots = difference(themes, screenshots);
52 const diffScreenshotsThemes = difference(screenshots, themes);
54 if (diffThemesScreenshots.size > 0) {
55 message.push(`Theme screenshots not found: ${Array.from(diffThemesScreenshots)}.`);
56 isValidationPassed = false;
58 if (diffScreenshotsThemes.size > 0) {
59 message.push(`Theme screenshots not removed: ${Array.from(diffScreenshotsThemes)}.`);
60 isValidationPassed = false;
63 if (!isValidationPassed) {
64 message.push('Theme thumbnails validation failed.');
66 message.push('Theme thumbnails validation completed.');
70 path: 'validate_theme.txt',
71 data: message.join('\n')
75 if (process.env.CI) hexo.extend.generator.register('validate_theme', validateTheme);