'use strict'; const {promisify} = require('util'); const path = require('path'); const childProcess = require('child_process'); const isWsl = require('is-wsl'); const pExecFile = promisify(childProcess.execFile); // Convert a path from WSL format to Windows format: // `/mnt/c/Program Files/Example/MyApp.exe` → `C:\Program Files\Example\MyApp.exe`` const wslToWindowsPath = async path => { const {stdout} = await pExecFile('wslpath', ['-w', path]); return stdout.trim(); }; module.exports = async (target, options) => { if (typeof target !== 'string') { throw new TypeError('Expected a `target`'); } options = { wait: false, ...options }; let command; let appArguments = []; const cliArguments = []; const childProcessOptions = {}; if (Array.isArray(options.app)) { appArguments = options.app.slice(1); options.app = options.app[0]; } if (process.platform === 'darwin') { command = 'open'; if (options.wait) { cliArguments.push('-W'); } if (options.app) { cliArguments.push('-a', options.app); } } else if (process.platform === 'win32' || isWsl) { command = 'cmd' + (isWsl ? '.exe' : ''); cliArguments.push('/c', 'start', '""', '/b'); target = target.replace(/&/g, '^&'); if (options.wait) { cliArguments.push('/wait'); } if (options.app) { if (isWsl && options.app.startsWith('/mnt/')) { const windowsPath = await wslToWindowsPath(options.app); options.app = windowsPath; } cliArguments.push(options.app); } if (appArguments.length > 0) { cliArguments.push(...appArguments); } } else { if (options.app) { command = options.app; } else { const useSystemXdgOpen = process.versions.electron || process.platform === 'android'; command = useSystemXdgOpen ? 'xdg-open' : path.join(__dirname, 'xdg-open'); } if (appArguments.length > 0) { cliArguments.push(...appArguments); } if (!options.wait) { // `xdg-open` will block the process unless stdio is ignored // and it's detached from the parent even if it's unref'd. childProcessOptions.stdio = 'ignore'; childProcessOptions.detached = true; } } cliArguments.push(target); if (process.platform === 'darwin' && appArguments.length > 0) { cliArguments.push('--args', ...appArguments); } const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions); if (options.wait) { return new Promise((resolve, reject) => { subprocess.once('error', reject); subprocess.once('close', exitCode => { if (exitCode > 0) { reject(new Error(`Exited with code ${exitCode}`)); return; } resolve(subprocess); }); }); } subprocess.unref(); return subprocess; };