Skip to content

Instantly share code, notes, and snippets.

@VonHeikemen
Last active January 17, 2019 10:14
Show Gist options
  • Save VonHeikemen/6ec3543c4229e98963fd376f522bd81b to your computer and use it in GitHub Desktop.
Save VonHeikemen/6ec3543c4229e98963fd376f522bd81b to your computer and use it in GitHub Desktop.
Experimental webpack plugin to execute scripts using puppeteer
// based on egoist's work in @poi/plugin-puppet
// https://github.com/egoist/poi/tree/master/plugins/puppet
// npm install -D serve-static
const util = require('util');
const http = require('http');
const serveStatic = require('serve-static');
const MAGIC_LOG = '__PUPPET__';
const windowPuppet = `
window.puppet = {
exit(code = 0) {
console.log('${MAGIC_LOG}', 'exit', code)
},
}
`;
const createServer = (publicFolder) => {
const serve = serveStatic(
publicFolder,
{'index': ['index.html', 'index.htm']}
);
return http.createServer(serve);
}
const captureOutput = exitProcess => async (message) => {
const type = message.type();
const args = message.args();
const jsonArgs = await Promise.all(args.map(arg => arg.jsonValue()))
// Clean-up to enable garbage collection
args.forEach(arg => arg.dispose());
const text = util.format(...jsonArgs);
if (type === 'clear') {
return console.clear();
}
if (type === 'startGroupCollapsed') {
return console.groupCollapsed();
}
if (type === 'endGroup') {
return console.groupEnd();
}
if (!text) {
return;
}
if(jsonArgs[0] == MAGIC_LOG) {
const [, command, code] = jsonArgs;
return exitProcess(command, code);
}
if (type === 'error') {
console.error(text);
} else if (type === 'warning') {
console.warn(text);
} else if (type === 'debug') {
console.debug(text);
} else if (type === 'startGroup') {
console.group(text);
} else if (type === 'info') {
console.log(text);
} else {
console.log(text);
}
}
class Puppet {
constructor({ launchBrowser = null, port = 8080 }) {
this.launcher = launchBrowser;
this.port = port;
this.loadPage = this.loadPage.bind(this);
this.cleanUp = this.cleanUp.bind(this);
this.exit = this.exit.bind(this);
}
prepareServer() {
this.server = createServer(this.outputPath);
return new Promise(resolve => this.server.listen(this.port, resolve));
}
async spawnPuppet() {
this.browser = await this.launcher();
this.page = await this.browser.newPage();
this.page.on('console', captureOutput(this.exit));
await this.page.evaluateOnNewDocument(windowPuppet);
}
loadPage() {
if(this.started && this.isWatchMode) {
return this.page.reload();
}
if(this.server.listening) {
this.started = true;
return this.page.goto(`http://localhost:${this.port}`);
}
}
async cleanUp() {
await this.page.close();
await this.browser.close();
if(this.server.listening) {
await new Promise(resolve => this.server.close(resolve));
}
}
async exit(command, code) {
if(this.isWatchMode) {
return;
}
if(command == 'exit') {
await this.cleanUp();
process.exit(code);
}
}
async apply(compiler) {
if(typeof this.launcher !== 'function') {
return;
}
const {done, watchClose} = compiler.hooks;
this.isWatchMode = compiler.options.watch;
this.outputPath = compiler.options.output.path;
done.tap('puppet', this.loadPage);
watchClose.tap('puppet', this.cleanUp);
await this.prepareServer();
await this.spawnPuppet();
}
}
module.exports = Puppet;
@VonHeikemen
Copy link
Author

// example usage
// npm install -D webpack webpack-nano html-webpack-plugin puppeteer-core

const { join } = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const Puppet = require('./webpackPluginPuppet');
const argv = require('webpack-nano/argv');

const puppeteer = require('puppeteer-core');
const launchBrowser = () => puppeteer.launch({
  executablePath: process.env.CHROMIUM_PATH
});

const { watch = false } = argv;

module.exports = {
  mode: 'development',
	entry: join(__dirname, 'tests', 'index.js'),
  plugins: [
    new HtmlWebpackPlugin(),
    new Puppet({launchBrowser})
  ],
  watch,
  stats: 'minimal',
  node: { fs: 'empty' }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment