From 9dbce46a88cad20239e70f87ab125df63b815ae4 Mon Sep 17 00:00:00 2001 From: Kasim Ahmic Date: Sun, 17 Jan 2021 13:04:35 -0500 Subject: [PATCH] Add new takeScreenshots.js file Builds on the original take_screenshots.js and adds a few QOL improvements: - Uses yargs to parse the aruments and display help screen - Gives the user the ability to run the screenshots in parallel - Adds a log statement for when a directory doesn't exist and needs to be created - Adds cleaner logging for which screenshot is being taken - Adds a log statement telling the user how many screenshots are going to be taken - Simplifies execution by passing all arguments as arrays and using a forEach loop on all of them resulting in a single reusable method - Adds simple error handling to avoid UnhandledPromiseRejectionErrors and to auto close the program in the event of an exception - Adds a console warning to the code sample page that tells the user the font the've typed in might not be valid. This warning is piped into takeScreenshots.js during the Puppeteer run and displayed in the terminal --- README.md | 39 +++++- package.json | 10 +- src/assets/scripts/font-sample-loader.js | 4 +- takeScreenshots.js | 153 +++++++++++++++++++++++ 4 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 takeScreenshots.js diff --git a/README.md b/README.md index 56eca1e..1fc95ee 100644 --- a/README.md +++ b/README.md @@ -20,22 +20,51 @@ https://coding-fonts.css-tricks.com/ Then make a Pull Request for it. You should be able to see a built preview on Netlify as part of the PR. -## Commands +## NPM Commands Take all screenshots for one font: -``` +```bash FONT='font-name' npm run screenshots ``` Take all screenshots: -``` +```bash npm run screenshots:all ``` -Take all screenshots for one language: +Take specific language screenshots + +```bash +FONT='font-name' npm run screenshots:js +FONT='font-name' npm run screenshots:html +FONT='font-name' npm run screenshots:css +FONT='font-name' npm run screenshots:charmap +``` + +## Taking Screenshots Using takeScreenshots.js + +You can take control of the screenshots + +takeScreenshots accepts the follow arguments: +``` +Options: + --version Show version number + -f, --font Font to use for screenshots + -l, --lang Code sample to use for screenshot + -t, --theme Theme to use for screenshots + --parallel Run the screenshots in parallel + -h, --help Show help +``` + +`--font`, `--lang`, and `--theme` all accept the 'all' keyword to handle taking screenshots of all of their respective data points. + +The `--parallel` flag allows the running of Puppeteer in parallel which is useful if you need to repeatedly take screenshots of a font. Do note that this will spawn as many Puppeteer instances as you have the total permutations you request. +Take the following command for example. ``` -node take_screenshots.js --all-for-lang="html" +node takeScreenshots.js -f source-code-pro -l all -t all ``` + +It will spawn 8 Puppeteer instances (1 font * 4 languages * 2 themes = 8 instances) all at once to take the screenshots. This is usually fine and doesn't result in any issues but in the event you were to do this for all fonts as well, you'd spawn hundreds of instances which will almost always in a failure. diff --git a/package.json b/package.json index e160b48..c57c995 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ "dev": "ELEVENTY_ENV=development eleventy --serve & npm run sass:watch", "sass:watch": "sass --watch src/assets/styles/sass/style.scss:src/assets/styles/style.css", "screenshots": "npm run screenshots:js && npm run screenshots:html && npm run screenshots:css && npm run screenshots:charmap", - "screenshots:js": "node take_screenshots.js --lang js --theme light --font \"$FONT\" && node take_screenshots.js --lang js --theme dark --font \"$FONT\"", - "screenshots:html": "node take_screenshots.js --lang html --theme light --font \"$FONT\" && node take_screenshots.js --lang html --theme dark --font \"$FONT\"", - "screenshots:css": "node take_screenshots.js --lang css --theme light --font \"$FONT\" && node take_screenshots.js --lang css --theme dark --font \"$FONT\"", - "screenshots:charmap": "node take_screenshots.js --lang charmap --theme light --font \"$FONT\" && node take_screenshots.js --lang charmap --theme dark --font \"$FONT\"", - "screenshots:all": "node take_screenshots.js --all true" + "screenshots:js": "node takeScreenshots.js --lang js --theme all --font \"$FONT\"", + "screenshots:html": "node takeScreenshots.js --lang html --theme all --font \"$FONT\"", + "screenshots:css": "node takeScreenshots.js --lang css --theme all --font \"$FONT\"", + "screenshots:charmap": "node takeScreenshots.js --lang charmap --theme all --font \"$FONT\"", + "screenshots:all": "node takeScreenshots.js --font all --lang all --theme all" }, "repository": { "type": "git", diff --git a/src/assets/scripts/font-sample-loader.js b/src/assets/scripts/font-sample-loader.js index 6a0c7b0..518abdc 100644 --- a/src/assets/scripts/font-sample-loader.js +++ b/src/assets/scripts/font-sample-loader.js @@ -43,4 +43,6 @@ if (fontInfo) { }); document.body.style.fontFamily = fontInfo.title; pre.style.fontFamily = fontInfo.title; -} +} else { + console.warn(`${font} does not appear to be a valid font. Please check the font name.`); +} \ No newline at end of file diff --git a/takeScreenshots.js b/takeScreenshots.js new file mode 100644 index 0000000..ad61f62 --- /dev/null +++ b/takeScreenshots.js @@ -0,0 +1,153 @@ +const samples = require('./src/_data/samples.json'); +const puppeteer = require('puppeteer'); +const { exit } = require('process'); +const yargs = require('yargs'); +const fs = require('fs'); + +const args = yargs(process.argv.slice(2)) + .usage('Usage: $0 [options]') + + .describe('f', 'Font to use for screenshots') + .alias('f', 'font') + + .describe('l', 'Code sample to use for screenshot') + .alias('l', 'lang') + + .describe('t', 'Theme to use for screenshots') + .alias('t', 'theme') + + .describe('parallel', 'Run the screenshots in parallel\n ~ Be careful when running too many screenshots at once.') + + .help('h') + .alias('h', 'help') + + .example('node $0 -f source-code-pro -l js -t dark', 'Take a screenshot of the Source Code Pro font, using the JavaScript code sample, and the dark theme.\n') + .example('node $0 -f menlo -l all -t light', 'Take a screenshot of the Menlo font, using all of the code samples, and the light theme.\n') + .example('node $0 -f all -l all -t all', 'Take a screenshot of all of the fonts, using all of the code samples, and all of the themes.\n') + .example('node $0 -f input -l all -t all --parallel', 'Take a screenshot of the Input font, using all of the code samples, all of the themes, and do so in parallel.') + + .demandOption(['lang', 'font', 'theme']) + + .wrap(115) + .epilog('Developed for Coding Fonts (https://coding-fonts.css-tricks.com/)') + .argv; + +const buildFontDirectories = (fonts) => { + fonts.forEach((font) => { + const directory = `src/screenshots/${font}`; + + if (!fs.existsSync(directory)) { + console.log(`${directory} does not exist. Making directory...`); + fs.mkdirSync(directory); + } + }); +}; + +const takeScreenshots = async (font, lang, theme) => { + console.log(`Taking screenshot for:`); + console.log(` => Font:\t${font}`); + console.log(` => Language:\t${lang}`); + console.log(` => Theme:\t${theme}`); + + const browser = await puppeteer.launch({ + args: ['--no-sandbox', '--disable-setuid-sandbox'] + }); + + const page = await browser.newPage(); + + await page.setViewport({ + width: 800, + height: 600, + deviceScaleFactor: 2 + }); + + page.on('console', msg => console.warn('Browser Log:', msg.text())); + + await page.goto( + `http://localhost:8080/code_samples/${lang}?font=${font}&theme=${theme}`, + { + waitUntil: 'networkidle0', + timeout: 100000 + } + ); + + await page.screenshot({ + path: `src/screenshots/${font}/${lang}-${theme}.png` + }); + + await browser.close(); +}; + +const parseInputs = (fonts, langs, themes) => { + fonts = fonts === 'all' + ? fs.readdirSync('./src/fonts') + .filter((file) => file.endsWith('.md')) + .map((file) => file.replace('.md', '').toLocaleLowerCase()) + : [fonts.toLocaleLowerCase()] + + langs = langs === 'all' + ? samples.languages.map(lang => lang.value) + : [langs]; + + themes = themes === 'all' + ? samples.themes + : [themes]; + + return [fonts, langs, themes]; +}; + +const run = async (fonts, langs, themes, parallel) => { + console.log(`Running in ${parallel ? 'parallel' : 'serial'} mode`); + console.log(`Taking ${fonts.length * langs.length * themes.length} screenshots\n`); + + const asyncForEach = async (array, callback) => { + for (let i = 0; i < array.length; i++) { + await callback(array[i], i, array); + } + }; + + if (parallel) { + fonts.forEach(async (font) => { + langs.forEach(async (lang) => { + themes.forEach(async (theme) => { + try { + await takeScreenshots(font, lang, theme); + } catch (e) { + console.error('\nAn error occurred. Please review the stack trace below and report the issue if necessary.\n'); + console.error(e); + exit(); + } + }); + }); + }); + } else { + await asyncForEach(fonts, async (font) => { + await asyncForEach(langs, async (lang) => { + await asyncForEach(themes, async (theme) => { + try { + await takeScreenshots(font, lang, theme); + } catch (e) { + console.error('\nAn error occurred. Please review the stack trace below and report the issue if necessary.\n'); + console.error(e); + exit(); + } + }); + }); + }); + } +} + +const main = async () => { + let fonts = args.font; + let langs = args.lang; + let themes = args.theme; + let parallel = args.parallel; + + [fonts, langs, themes] = parseInputs(fonts, langs, themes); + + buildFontDirectories(fonts); + + run(fonts, langs, themes, parallel); +}; + +main();