diff --git a/app/plugins.ts b/app/plugins.ts index 012c83c46c1d..f4cdefbcfc91 100644 --- a/app/plugins.ts +++ b/app/plugins.ts @@ -1,7 +1,7 @@ /* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -import {app, dialog, BrowserWindow, App} from 'electron'; +import {app, dialog, BrowserWindow, App, ipcMain} from 'electron'; import {resolve, basename} from 'path'; import {writeFileSync} from 'fs'; import Config from 'electron-store'; @@ -15,6 +15,8 @@ import {install} from './plugins/install'; import {plugs} from './config/paths'; import mapKeys from './utils/map-keys'; import {configOptions} from '../lib/config'; +import {promisify} from 'util'; +import {exec, execFile} from 'child_process'; // local storage const cache = new Config(); @@ -449,3 +451,13 @@ export const decorateSessionClass = (Session: T): T => { }; export {toDependencies as _toDependencies}; + +ipcMain.handle('child_process.exec', (event, args) => { + const {command, options} = args; + return promisify(exec)(command, options); +}); + +ipcMain.handle('child_process.execFile', (event, _args) => { + const {file, args, options} = _args; + return promisify(execFile)(file, args, options); +}); diff --git a/lib/utils/ipc-child-process.ts b/lib/utils/ipc-child-process.ts new file mode 100644 index 000000000000..6f1dae222357 --- /dev/null +++ b/lib/utils/ipc-child-process.ts @@ -0,0 +1,58 @@ +import {ipcRenderer} from 'electron'; + +export function exec(command: string, options?: any, callback?: (..._args: any) => void) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + ipcRenderer.invoke('child_process.exec', {command, options}).then( + ({stdout, stderr}) => callback?.(null, stdout, stderr), + (error) => callback?.(error, '', '') + ); +} + +export function execSync() { + console.error('Calling execSync from renderer is disabled'); +} + +export function execFile(file: string, args?: any, options?: any, callback?: (..._args: any) => void) { + if (typeof options === 'function') { + callback = options; + options = null; + } + if (typeof args === 'function') { + callback = args; + args = null; + options = null; + } + ipcRenderer.invoke('child_process.execFile', {file, args, options}).then( + ({stdout, stderr}) => callback?.(null, stdout, stderr), + (error) => callback?.(error, '', '') + ); +} + +export function execFileSync() { + console.error('Calling execFileSync from renderer is disabled'); +} + +export function spawn() { + console.error('Calling spawn from renderer is disabled'); +} + +export function spawnSync() { + console.error('Calling spawnSync from renderer is disabled'); +} + +export function fork() { + console.error('Calling fork from renderer is disabled'); +} + +export default { + exec, + execSync, + execFile, + execFileSync, + spawn, + spawnSync, + fork +}; diff --git a/lib/utils/plugins.ts b/lib/utils/plugins.ts index 6e37f7862d9e..3c5b95c235ac 100644 --- a/lib/utils/plugins.ts +++ b/lib/utils/plugins.ts @@ -28,6 +28,8 @@ import { } from '../hyper'; import {Middleware} from 'redux'; import {ObjectTypedKeys} from './object'; +import IPCChildProcess from './ipc-child-process'; +import ChildProcess from 'child_process'; // remote interface to `../plugins` const plugins = remote.require('./plugins') as typeof import('../../app/plugins'); @@ -182,6 +184,8 @@ Module._load = function _load(path: string) { return Notification; case 'hyper/decorate': return decorate; + case 'child_process': + return process.platform === 'darwin' ? IPCChildProcess : ChildProcess; default: // eslint-disable-next-line prefer-rest-params return originalLoad.apply(this, arguments);