'use strict'; /* Drop this dependency once node.js 12 is required. */ const fromEntries = require('fromentries'); const state = getState(1); function getState(version) { const stateId = Symbol.for('process-on-spawn@*:singletonId'); /* istanbul ignore next: cannot cover this once nyc depends on this module */ if (stateId in global === false) { /* Hopefully version and unwrap forward compatibility is never actually needed */ Object.defineProperty(global, stateId, { writable: true, value: { version, listeners: [], unwrap: wrapSpawnFunctions() } }); } return global[stateId]; } function wrappedSpawnFunction(fn) { return function (options) { let env = fromEntries( options.envPairs.map(nvp => nvp.split(/^([^=]*)=/).slice(1)) ); const opts = Object.create(null, { env: { enumerable: true, get() { return env; }, set(value) { if (!value || typeof value !== 'object') { throw new TypeError('env must be an object'); } env = value; } }, cwd: { enumerable: true, get() { return options.cwd || process.cwd(); } } }); const args = [...options.args]; Object.freeze(args); Object.assign(opts, { execPath: options.file, args, detached: Boolean(options.detached), uid: options.uid, gid: options.gid, windowsVerbatimArguments: Boolean(options.windowsVerbatimArguments), windowsHide: Boolean(options.windowsHide) }); Object.freeze(opts); state.listeners.forEach(listener => { listener(opts); }); options.envPairs = Object.entries(opts.env).map(([name, value]) => `${name}=${value}`); return fn.call(this, options); }; } function wrapSpawnFunctions() { const {ChildProcess} = require('child_process'); /* eslint-disable-next-line node/no-deprecated-api */ const spawnSyncBinding = process.binding('spawn_sync'); const originalSync = spawnSyncBinding.spawn; const originalAsync = ChildProcess.prototype.spawn; spawnSyncBinding.spawn = wrappedSpawnFunction(spawnSyncBinding.spawn); ChildProcess.prototype.spawn = wrappedSpawnFunction(ChildProcess.prototype.spawn); /* istanbul ignore next: forward compatibility code */ return () => { spawnSyncBinding.spawn = originalSync; ChildProcess.prototype.spawn = originalAsync; }; } module.exports = { addListener(listener) { state.listeners.push(listener); }, prependListener(listener) { state.listeners.unshift(listener); }, removeListener(listener) { const idx = state.listeners.indexOf(listener); if (idx !== -1) { state.listeners.splice(idx, 1); } }, removeAllListeners() { state.listeners = []; } };