123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- const path = require('path');
- const childProcess = require('child_process');
- const {promises: fs, constants: fsConstants} = require('fs');
- const isWsl = require('is-wsl');
- const isDocker = require('is-docker');
- const defineLazyProperty = require('define-lazy-prop');
- // Path to included `xdg-open`.
- const localXdgOpenPath = path.join(__dirname, 'xdg-open');
- const {platform, arch} = process;
- // Podman detection
- const hasContainerEnv = () => {
- try {
- fs.statSync('/run/.containerenv');
- return true;
- } catch {
- return false;
- }
- };
- let cachedResult;
- function isInsideContainer() {
- if (cachedResult === undefined) {
- cachedResult = hasContainerEnv() || isDocker();
- }
- return cachedResult;
- }
- /**
- 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 fs.access(configFilePath, fsConstants.F_OK);
- isConfigFileExists = true;
- } catch {}
- if (!isConfigFileExists) {
- return defaultMountPoint;
- }
- const configContent = await fs.readFile(configFilePath, {encoding: 'utf8'});
- const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
- if (!configMountPoint) {
- return defaultMountPoint;
- }
- mountPoint = configMountPoint.groups.mountPoint.trim();
- mountPoint = mountPoint.endsWith('/') ? mountPoint : `${mountPoint}/`;
- return mountPoint;
- };
- })();
- const pTryEach = async (array, mapper) => {
- let latestError;
- for (const item of array) {
- try {
- return await mapper(item); // eslint-disable-line no-await-in-loop
- } catch (error) {
- latestError = error;
- }
- }
- throw latestError;
- };
- const baseOpen = async options => {
- options = {
- wait: false,
- background: false,
- newInstance: false,
- allowNonzeroExitCode: false,
- ...options
- };
- if (Array.isArray(options.app)) {
- return pTryEach(options.app, singleApp => baseOpen({
- ...options,
- app: singleApp
- }));
- }
- let {name: app, arguments: appArguments = []} = options.app || {};
- appArguments = [...appArguments];
- if (Array.isArray(app)) {
- return pTryEach(app, appName => baseOpen({
- ...options,
- app: {
- name: appName,
- arguments: appArguments
- }
- }));
- }
- let command;
- const cliArguments = [];
- const childProcessOptions = {};
- if (platform === 'darwin') {
- command = 'open';
- if (options.wait) {
- cliArguments.push('--wait-apps');
- }
- if (options.background) {
- cliArguments.push('--background');
- }
- if (options.newInstance) {
- cliArguments.push('--new');
- }
- if (app) {
- cliArguments.push('-a', app);
- }
- } else if (platform === 'win32' || (isWsl && !isInsideContainer() && !app)) {
- 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');
- if (options.target) {
- appArguments.unshift(options.target);
- }
- } else if (options.target) {
- encodedArguments.push(`"${options.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.
- options.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 fs.access(localXdgOpenPath, fsConstants.X_OK);
- exeLocalXdgOpen = true;
- } catch {}
- const useSystemXdgOpen = process.versions.electron ||
- 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;
- }
- }
- if (options.target) {
- cliArguments.push(options.target);
- }
- if (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;
- };
- const open = (target, options) => {
- if (typeof target !== 'string') {
- throw new TypeError('Expected a `target`');
- }
- return baseOpen({
- ...options,
- target
- });
- };
- const openApp = (name, options) => {
- if (typeof name !== 'string') {
- throw new TypeError('Expected a `name`');
- }
- const {arguments: appArguments = []} = options || {};
- if (appArguments !== undefined && appArguments !== null && !Array.isArray(appArguments)) {
- throw new TypeError('Expected `appArguments` as Array type');
- }
- return baseOpen({
- ...options,
- app: {
- name,
- arguments: appArguments
- }
- });
- };
- function detectArchBinary(binary) {
- if (typeof binary === 'string' || Array.isArray(binary)) {
- return binary;
- }
- const {[arch]: archBinary} = binary;
- if (!archBinary) {
- throw new Error(`${arch} is not supported`);
- }
- return archBinary;
- }
- function detectPlatformBinary({[platform]: platformBinary}, {wsl}) {
- if (wsl && isWsl) {
- return detectArchBinary(wsl);
- }
- if (!platformBinary) {
- throw new Error(`${platform} is not supported`);
- }
- return detectArchBinary(platformBinary);
- }
- const apps = {};
- defineLazyProperty(apps, 'chrome', () => detectPlatformBinary({
- darwin: 'google chrome',
- win32: 'chrome',
- linux: ['google-chrome', 'google-chrome-stable', 'chromium']
- }, {
- wsl: {
- ia32: '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe',
- x64: ['/mnt/c/Program Files/Google/Chrome/Application/chrome.exe', '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe']
- }
- }));
- defineLazyProperty(apps, 'firefox', () => detectPlatformBinary({
- darwin: 'firefox',
- win32: 'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
- linux: 'firefox'
- }, {
- wsl: '/mnt/c/Program Files/Mozilla Firefox/firefox.exe'
- }));
- defineLazyProperty(apps, 'edge', () => detectPlatformBinary({
- darwin: 'microsoft edge',
- win32: 'msedge',
- linux: ['microsoft-edge', 'microsoft-edge-dev']
- }, {
- wsl: '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
- }));
- open.apps = apps;
- open.openApp = openApp;
- module.exports = open;
|