You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					135 lines
				
				3.2 KiB
			
		
		
			
		
	
	
					135 lines
				
				3.2 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								'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 pAccess = promisify(fs.access);
							 | 
						||
| 
								 | 
							
								const pExecFile = promisify(childProcess.execFile);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Path to included `xdg-open`
							 | 
						||
| 
								 | 
							
								const localXdgOpenPath = path.join(__dirname, 'xdg-open');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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,
							 | 
						||
| 
								 | 
							
										background: 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('--wait-apps');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (options.background) {
							 | 
						||
| 
								 | 
							
											cliArguments.push('--background');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										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 {
							 | 
						||
| 
								 | 
							
											// 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 (error) {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											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 (exitCode > 0) {
							 | 
						||
| 
								 | 
							
													reject(new Error(`Exited with code ${exitCode}`));
							 | 
						||
| 
								 | 
							
													return;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												resolve(subprocess);
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									subprocess.unref();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return subprocess;
							 | 
						||
| 
								 | 
							
								};
							 |