UNPKG

5.48 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3const path = require('path');
4const os = require('os');
5const program = require('commander');
6const chalk = require('chalk');
7const ora = require('ora');
8const detect = require('detect-port');
9const imagemin = require('imagemin');
10const imageminMozjpeg = require('imagemin-mozjpeg');
11const imageminPngquant = require('imagemin-pngquant');
12
13const createServer = require('../utils/createServer');
14const getPuppeteer = require('../utils/getPuppeteer');
15const packageJSON = require('../package.json');
16
17const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
18
19const cwd = process.cwd();
20const DEFAULT_PORT = 8100;
21
22exec();
23
24async function exec() {
25 try {
26 program
27 .version(packageJSON.version)
28 .usage('-u https://www.example.com')
29 .option('-u, --url <url>', 'The target url or path to local server')
30 .option(
31 '-l, --local [local]',
32 'Set up a local server in [local] directory and take screenshot, defaults set up in `./`',
33 )
34 .option('-s, --selector <selector>', 'Select a element through CSS selector')
35 .option('-t, --timeout <timeout>', 'screenshot with a delay')
36 .option('-o, --output <output>', 'Output path')
37 .parse(process.argv);
38
39 const { url, selector, local, timeout } = program;
40 const output = program.output || path.join(cwd, 'screenshot.png');
41
42 // compatiable `-s mountNode` to fix: https://github.com/alibaba/ice/issues/2641
43 const formatedSelector = selector && !/^(#|\.)/.test(selector) && selector !== 'body' ? `#${selector}` : selector;
44
45 if (!url && !local) {
46 console.log(chalk.red('The -u or -l is required! Using the following command:'));
47 console.log(chalk.red('screenshot -u https://www.example.com\n'));
48 program.help();
49 }
50
51 if (local) {
52 const port = await detect(DEFAULT_PORT);
53 const serverPath = local === true ? cwd : local;
54 await screenshotWithLocalServer(serverPath, port, url, formatedSelector, output, timeout);
55 } else {
56 await screenshot(url, formatedSelector, output, timeout);
57 }
58 } catch (err) {
59 console.error(err);
60 process.exit(1);
61 }
62}
63
64/**
65 * take a screenshot with local server
66 *
67 * @param {string} serverPath local server directory
68 * @param {number} port server port
69 * @param {string} targetUrl the target url
70 * @param {string} selector the target CSS selector
71 * @param {string} output output path
72 */
73async function screenshotWithLocalServer(serverPath, port, targetUrl, selector, output, timeout) {
74 targetUrl = targetUrl ? `http://127.0.0.1:${port}${targetUrl}` : `http://127.0.0.1:${port}/build/index.html`; // default screenshot target
75
76 const server = createServer(serverPath, port);
77 console.log(chalk.white(`Create local server with port ${port}`));
78 console.log(chalk.white(`The screenshot target url: ${targetUrl}`));
79
80 await screenshot(targetUrl, selector, output, timeout);
81
82 server.close();
83}
84
85/**
86 * take a screenshot of web page
87 *
88 * @param {string} url the target url
89 * @param {string} selector screenshot target CSS selector
90 * @param {string} output output path
91 */
92async function screenshot(url, selector, output, timeout) {
93 // a terminal spinner
94 const spinner = ora('screenshoting ...').start();
95
96 try {
97 const puppeteer = await getPuppeteer();
98
99 // start puppeteer
100 const browser = await puppeteer.launch(
101 /freebsd|linux/.test(os.platform) ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] } : {},
102 );
103
104 // create a new page
105 const page = await browser.newPage();
106
107 // set page's viewport
108 page.setViewport({
109 width: 1240,
110 height: 600,
111 deviceScaleFactor: 2,
112 });
113
114 // visit the target url
115 await page.goto(url);
116
117 if (timeout) {
118 await sleep(timeout);
119 }
120
121 // screenshot a element through CSS selector;
122 if (selector) {
123 const el = await page.$(selector);
124
125 if (!el) {
126 throw Error(`Could not find element that matches selector: ${selector}.`);
127 }
128
129 await el.screenshot({ path: output });
130 } else {
131 // screenshot full page
132 await page.screenshot({ path: output });
133 }
134
135 const outputDir = path.dirname(output);
136 // minify screenshot
137 await minifyImg(output, outputDir);
138
139 // close chromium
140 await browser.close();
141
142 spinner.succeed(chalk.green('Screenshot success!'));
143 console.log(chalk.green(`Screenshot output path: ${output}`));
144 } catch (err) {
145 spinner.fail(chalk.red('Screenshot fail!'));
146
147 // chromium not download error
148 // stdout reinstall puppeteer tips.
149 if (err.message === 'Chromium revision is not downloaded. Run "npm install" or "yarn install"') {
150 console.log(chalk.red('\n\nPuppeteer Install fail. \nPlease install puppeteer using the following commands:'));
151 console.log(chalk.white('\n npm uninstall puppeteer -g'));
152 console.log(
153 chalk.white(
154 '\n PUPPETEER_DOWNLOAD_HOST=https://storage.googleapis.com.cnpmjs.org npm i puppeteer -g --registry=https://registry.npmmirror.com',
155 ),
156 );
157 console.log(chalk.white('\n screenshot -u http://www.example.com\n'));
158 } else {
159 console.error(err);
160 }
161 process.exit(1);
162 }
163}
164
165/**
166 * minify an image
167 *
168 * @param {String} imgPath
169 * @param {*} outputDir output dir
170 * @returns
171 */
172async function minifyImg(imgPath, outputDir) {
173 return imagemin([imgPath], outputDir, {
174 plugins: [imageminMozjpeg(), imageminPngquant()],
175 });
176}