123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 'use strict';
- const {promisify} = require('util');
- const path = require('path');
- const childProcess = require('child_process');
- const fs = require('fs');
- const isWsl = require('is-wsl');
- const isDocker = require('is-docker');
- const pAccess = promisify(fs.access);
- const pReadFile = promisify(fs.readFile);
- // Path to included `xdg-open`.
- const localXdgOpenPath = path.join(__dirname, 'xdg-open');
- /**
- Get the mount point for fixed drives in WSL.
- @inner
- @returns {string} The mount point.
- */
- const getWslDrivesMountPoint = (() => {
- // Default value for "root" param
- // according to https://docs.microsoft.com/en-us/windows/wsl/wsl-config
- const defaultMountPoint = '/mnt/';
- let mountPoint;
- return async function () {
- if (mountPoint) {
- // Return memoized mount point value
- return mountPoint;
- }
- const configFilePath = '/etc/wsl.conf';
- let isConfigFileExists = false;
- try {
- await pAccess(configFilePath, fs.constants.F_OK);
- isConfigFileExists = true;
- } catch (_) {}
- if (!isConfigFileExists) {
- return defaultMountPoint;
- }
- const configContent = await pReadFile(configFilePath, {encoding: 'utf8'});
- const configMountPoint = /root\s*=\s*(.*)/g.exec(configContent);
- if (!configMountPoint) {
- return defaultMountPoint;
- }
- mountPoint = configMountPoint[1].trim();
- mountPoint = mountPoint.endsWith('/') ? mountPoint : mountPoint + '/';
- return mountPoint;
- };
- })();
- module.exports = async (target, options) => {
- if (typeof target !== 'string') {
- throw new TypeError('Expected a `target`');
- }
- options = {
- wait: false,
- background: false,
- allowNonzeroExitCode: false,
- ...options
- };
- let command;
- let {app} = options;
- let appArguments = [];
- const cliArguments = [];
- const childProcessOptions = {};
- if (Array.isArray(app)) {
- appArguments = app.slice(1);
- app = app[0];
- }
- if (process.platform === 'darwin') {
- command = 'open';
- if (options.wait) {
- cliArguments.push('--wait-apps');
- }
- if (options.background) {
- cliArguments.push('--background');
- }
- if (app) {
- cliArguments.push('-a', app);
- }
- } else if (process.platform === 'win32' || (isWsl && !isDocker())) {
- const mountPoint = await getWslDrivesMountPoint();
- command = isWsl ?
- `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` :
- `${process.env.SYSTEMROOT}\\System32\\WindowsPowerShell\\v1.0\\powershell`;
- cliArguments.push(
- '-NoProfile',
- '-NonInteractive',
- '–ExecutionPolicy',
- 'Bypass',
- '-EncodedCommand'
- );
- if (!isWsl) {
- childProcessOptions.windowsVerbatimArguments = true;
- }
- const encodedArguments = ['Start'];
- if (options.wait) {
- encodedArguments.push('-Wait');
- }
- if (app) {
- // Double quote with double quotes to ensure the inner quotes are passed through.
- // Inner quotes are delimited for PowerShell interpretation with backticks.
- encodedArguments.push(`"\`"${app}\`""`, '-ArgumentList');
- appArguments.unshift(target);
- } else {
- encodedArguments.push(`"${target}"`);
- }
- if (appArguments.length > 0) {
- appArguments = appArguments.map(arg => `"\`"${arg}\`""`);
- encodedArguments.push(appArguments.join(','));
- }
- // Using Base64-encoded command, accepted by PowerShell, to allow special characters.
- target = Buffer.from(encodedArguments.join(' '), 'utf16le').toString('base64');
- } else {
- if (app) {
- command = app;
- } else {
- // When bundled by Webpack, there's no actual package file path and no local `xdg-open`.
- const isBundled = !__dirname || __dirname === '/';
- // Check if local `xdg-open` exists and is executable.
- let exeLocalXdgOpen = false;
- try {
- await pAccess(localXdgOpenPath, fs.constants.X_OK);
- exeLocalXdgOpen = true;
- } catch (_) {}
- const useSystemXdgOpen = process.versions.electron ||
- process.platform === 'android' || isBundled || !exeLocalXdgOpen;
- command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;
- }
- 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 (options.allowNonzeroExitCode && exitCode > 0) {
- reject(new Error(`Exited with code ${exitCode}`));
- return;
- }
- resolve(subprocess);
- });
- });
- }
- subprocess.unref();
- return subprocess;
- };
|