diff --git a/packages/server/lib/plugins/index.js b/packages/server/lib/plugins/index.js index 68b88d7df069..15f3b7536ad5 100644 --- a/packages/server/lib/plugins/index.js +++ b/packages/server/lib/plugins/index.js @@ -1,155 +1,174 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const _ = require("lodash"); -const cp = require("child_process"); -const path = require("path"); -const debug = require("debug")("cypress:server:plugins"); -const Promise = require("bluebird"); -const errors = require("../errors"); -const util = require("./util"); - -let pluginsProcess = null; -let registeredEvents = {}; -let handlers = []; - -const register = function(event, callback) { - debug(`register event '${event}'`); +const _ = require('lodash') +const cp = require('child_process') +const path = require('path') +const debug = require('debug')('cypress:server:plugins') +const Promise = require('bluebird') +const errors = require('../errors') +const util = require('./util') + +let pluginsProcess = null +let registeredEvents = {} +let handlers = [] + +const register = (event, callback) => { + debug(`register event '${event}'`) if (!_.isString(event)) { - throw new Error(`The plugin register function must be called with an event as its 1st argument. You passed '${event}'.`); + throw new Error(`The plugin register function must be called with an event as its 1st argument. You passed '${event}'.`) } if (!_.isFunction(callback)) { - throw new Error(`The plugin register function must be called with a callback function as its 2nd argument. You passed '${callback}'.`); + throw new Error(`The plugin register function must be called with a callback function as its 2nd argument. You passed '${callback}'.`) } - return registeredEvents[event] = callback; -}; + registeredEvents[event] = callback +} -module.exports = { - //# for testing - _setPluginsProcess(_pluginsProcess) { - return pluginsProcess = _pluginsProcess; - }, +const getPluginPid = () => { + if (pluginsProcess) { + return pluginsProcess.pid + } +} + +const registerHandler = (handler) => { + handlers.push(handler) +} + +const init = (config, options) => { + debug('plugins.init', config.pluginsFile) + + return new Promise((resolve, reject) => { + if (!config.pluginsFile) { + return resolve() + } - getPluginPid() { if (pluginsProcess) { - return pluginsProcess.pid; + debug('kill existing plugins process') + pluginsProcess.kill() } - }, - registerHandler(handler) { - return handlers.push(handler); - }, + registeredEvents = {} + + const childIndexFilename = path.join(__dirname, 'child', 'index.js') + const childArguments = ['--file', config.pluginsFile] + const childOptions = { + stdio: 'inherit', + } + + if (config.resolvedNodePath) { + debug('launching using custom node version %o', _.pick(config, ['resolvedNodePath', 'resolvedNodeVersion'])) + childOptions.execPath = config.resolvedNodePath + } + + debug('forking to run %s', childIndexFilename) + pluginsProcess = cp.fork(childIndexFilename, childArguments, childOptions) + const ipc = util.wrapIpc(pluginsProcess) + + for (let handler of handlers) { + handler(ipc) + } + + ipc.send('load', config) + + ipc.on('loaded', (newCfg, registrations) => { + _.each(registrations, (registration) => { + debug('register plugins process event', registration.event, 'with id', registration.eventId) - init(config, options) { - debug("plugins.init", config.pluginsFile); + register(registration.event, (...args) => { + return util.wrapParentPromise(ipc, registration.eventId, (invocationId) => { + debug('call event', registration.event, 'for invocation id', invocationId) + const ids = { + eventId: registration.eventId, + invocationId, + } + + ipc.send('execute', registration.event, ids, args) + }) + }) + }) + + debug('resolving with new config %o', newCfg) + + resolve(newCfg) + }) - return new Promise(function(resolve, reject) { - if (!config.pluginsFile) { return resolve(); } + ipc.on('load:error', (type, ...args) => { + debug('load:error %s, rejecting', type) - if (pluginsProcess) { - debug("kill existing plugins process"); - pluginsProcess.kill(); + reject(errors.get(type, ...args)) + }) + + const killPluginsProcess = () => { + pluginsProcess && pluginsProcess.kill() + pluginsProcess = null + } + + const handleError = (err) => { + debug('plugins process error:', err.stack) + if (!pluginsProcess) { + return // prevent repeating this in case of multiple errors } - registeredEvents = {}; + killPluginsProcess() + err = errors.get('PLUGINS_ERROR', err.annotated || err.stack || err.message) + err.title = 'Error running plugin' - const childIndexFilename = path.join(__dirname, "child", "index.js"); - const childArguments = ["--file", config.pluginsFile]; - const childOptions = { - stdio: "inherit" - }; + return options.onError(err) + } - if (config.resolvedNodePath) { - debug("launching using custom node version %o", _.pick(config, ['resolvedNodePath', 'resolvedNodeVersion'])); - childOptions.execPath = config.resolvedNodePath; + const handleWarning = function (warningErr) { + debug('plugins process warning:', warningErr.stack) + if (!pluginsProcess) { + return // prevent repeating this in case of multiple warnings } - debug("forking to run %s", childIndexFilename); - pluginsProcess = cp.fork(childIndexFilename, childArguments, childOptions); - const ipc = util.wrapIpc(pluginsProcess); + return options.onWarning(warningErr) + } - for (let handler of handlers) { handler(ipc); } + pluginsProcess.on('error', handleError) + ipc.on('error', handleError) + ipc.on('warning', handleWarning) - ipc.send("load", config); + // see timers/parent.js line #93 for why this is necessary + process.on('exit', killPluginsProcess) + }) +} - ipc.on("loaded", function(newCfg, registrations) { - _.each(registrations, function(registration) { - debug("register plugins process event", registration.event, "with id", registration.eventId); +const has = (event) => { + const isRegistered = !!registeredEvents[event] - return register(registration.event, (...args) => util.wrapParentPromise(ipc, registration.eventId, function(invocationId) { - debug("call event", registration.event, "for invocation id", invocationId); - const ids = { - eventId: registration.eventId, - invocationId - }; - return ipc.send("execute", registration.event, ids, args); - })); - }); - - debug("resolving with new config %o", newCfg); - return resolve(newCfg); - }); - - ipc.on("load:error", function(type, ...args) { - debug("load:error %s, rejecting", type); - return reject(errors.get(type, ...args)); - }); - - const killPluginsProcess = function() { - pluginsProcess && pluginsProcess.kill(); - return pluginsProcess = null; - }; - - const handleError = function(err) { - debug("plugins process error:", err.stack); - if (!pluginsProcess) { return; } //# prevent repeating this in case of multiple errors - killPluginsProcess(); - err = errors.get("PLUGINS_ERROR", err.annotated || err.stack || err.message); - err.title = "Error running plugin"; - return options.onError(err); - }; - - const handleWarning = function(warningErr) { - debug("plugins process warning:", warningErr.stack); - if (!pluginsProcess) { return; } //# prevent repeating this in case of multiple warnings - return options.onWarning(warningErr); - }; - - pluginsProcess.on("error", handleError); - ipc.on("error", handleError); - ipc.on("warning", handleWarning); - - //# see timers/parent.js line #93 for why this is necessary - return process.on("exit", killPluginsProcess); - }); - }, + debug('plugin event registered? %o', { + event, + isRegistered, + }) - register, + return isRegistered +} - has(event) { - const isRegistered = !!registeredEvents[event]; +const execute = (event, ...args) => { + debug(`execute plugin event '${event}' Node '${process.version}' with args: %o %o %o`, ...args) - debug("plugin event registered? %o", { - event, - isRegistered - }); + return registeredEvents[event](...args) +} - return isRegistered; - }, +const _reset = () => { + registeredEvents = {} + handlers = [] +} - execute(event, ...args) { - debug(`execute plugin event '${event}' Node '${process.version}' with args: %o %o %o`, ...args); - return registeredEvents[event](...args); - }, +const _setPluginsProcess = (_pluginsProcess) => { + pluginsProcess = _pluginsProcess +} - //# for testing purposes - _reset() { - registeredEvents = {}; - return handlers = []; - } -}; +module.exports = { + getPluginPid, + execute, + has, + init, + register, + registerHandler, + + // for testing purposes + _reset, + _setPluginsProcess, +}