source/index.js
- import {packageOf} from '@tech_query/node-toolkit';
-
- import {resolve} from 'url';
-
- import WebServer from 'koapache';
-
- import {watch} from 'chokidar';
-
- import {toString} from 'qrcode';
-
- import promisify from 'promisify-node';
-
- const QRCode = promisify( toString );
-
-
-
- const { env } = process, config = (packageOf('./test') || '').meta;
-
- const NPM_command = env.npm_lifecycle_script;
-
- var server, browser, page;
-
-
- /**
- * Wrapper of `Puppeteer` class
- */
- export default class PuppeteerBrowser {
- /**
- * @protected
- *
- * @param {string} [root] - Root path of the static site
- *
- * @return {Object} Server information
- */
- static async getServer(root) {
-
- return server || (
- server = await (new WebServer(root || '.')).workerHost()
- );
- }
-
- /**
- * @type {string}
- */
- static get browserName() {
-
- return (env.npm_config_PUPPETEER_BROWSER || 'chrome').trim();
- }
-
- /**
- * @type {string}
- */
- static get moduleName() {
-
- return 'puppeteer' + (map => {
-
- for (let name in map)
- if (name === this.browserName) return map[name];
-
- return '';
- })({
- chrome: '',
- firefox: '-fx',
- IE: '-ie'
- });
- }
-
- /**
- * @param {Object} [options] - https://pptr.dev/#?product=Puppeteer&version=v1.5.0&show=api-puppeteerlaunchoptions
- *
- * @return {Browser}
- */
- static async launch(options) {
-
- const Puppeteer = (await import( this.moduleName )).default;
-
- return await Puppeteer.launch( options );
- }
-
- /**
- * @return {string} https://pptr.dev/#?product=Puppeteer&version=v1.5.0&show=api-puppeteerexecutablepath
- */
- static executablePath() {
-
- return env['npm_config_' + this.browserName];
- }
-
- /**
- * @protected
- *
- * @param {boolean} [visible] - Browser visibility
- * (Visible mode will run slowly for seeing clearly)
- * @return {Browser}
- */
- static async getBrowser(visible) {
-
- if ( browser ) return browser;
-
- visible = (visible != null) ?
- visible : NPM_command.includes('--inspect');
-
- browser = await this.launch({
- executablePath: this.executablePath(),
- headless: ! visible,
- slowMo: visible ? 100 : 0
- });
-
- return browser.on('disconnected', () => browser = page = null);
- }
-
- /**
- * After files changed, the page will be focused & reloaded
- *
- * @param {string} path - Directory to watch recursively
- * @param {function} onChange - Call on files changed
- *
- * @return {FSWatcher}
- */
- static watch(path, onChange) {
-
- var listen;
-
- async function refresh() {
-
- await onChange();
-
- if ( page ) {
-
- await page.bringToFront();
-
- await page.reload();
-
- console.info(`[ Reload ] ${page.url()}`);
- }
-
- listen = false;
- }
-
- return watch( path ).on('change', () => {
-
- if (! listen) {
-
- listen = true;
-
- process.nextTick( refresh );
- }
- });
- }
-
- /**
- * @param {?string} root - Root path to start Web server, default to be `process.cwd()`
- * @param {?string} path - Path to open Web page
- * @param {function} [fileChange] - Do something between files changed & page reload
- * (Browser will be visible)
- * @return {Page} Resolve after `DOMContentLoaded` event fired
- */
- static async getPage(root, path, fileChange) {
-
- if ( page ) return page;
-
- path = path || '.';
-
- fileChange = (fileChange instanceof Function) ? fileChange : null;
-
- const server = path.indexOf('http') && await this.getServer( root );
-
- const URI = resolve(`http://${server.address}:${server.port}/`, path);
-
- if ( fileChange ) console.info(await QRCode( URI ));
-
- page = await (await this.getBrowser( fileChange )).newPage();
-
- await page.on('close', () => page = null).goto(server ? URI : path, {
- waitUntil: 'domcontentloaded'
- });
-
- if ( fileChange )
- this.watch(config.directories.lib || root, fileChange);
-
- return page;
- }
- }