UNPKG

2.69 kBJavaScriptView Raw
1'use strict';
2const {promisify} = require('util');
3const path = require('path');
4const childProcess = require('child_process');
5const isWsl = require('is-wsl');
6
7const pExecFile = promisify(childProcess.execFile);
8
9// Convert a path from WSL format to Windows format:
10// `/mnt/c/Program Files/Example/MyApp.exe` → `C:\Program Files\Example\MyApp.exe``
11const wslToWindowsPath = async path => {
12 const {stdout} = await pExecFile('wslpath', ['-w', path]);
13 return stdout.trim();
14};
15
16module.exports = async (target, options) => {
17 if (typeof target !== 'string') {
18 throw new TypeError('Expected a `target`');
19 }
20
21 options = {
22 wait: false,
23 ...options
24 };
25
26 let command;
27 let appArguments = [];
28 const cliArguments = [];
29 const childProcessOptions = {};
30
31 if (Array.isArray(options.app)) {
32 appArguments = options.app.slice(1);
33 options.app = options.app[0];
34 }
35
36 if (process.platform === 'darwin') {
37 command = 'open';
38
39 if (options.wait) {
40 cliArguments.push('-W');
41 }
42
43 if (options.app) {
44 cliArguments.push('-a', options.app);
45 }
46 } else if (process.platform === 'win32' || isWsl) {
47 command = 'cmd' + (isWsl ? '.exe' : '');
48 cliArguments.push('/c', 'start', '""', '/b');
49 target = target.replace(/&/g, '^&');
50
51 if (options.wait) {
52 cliArguments.push('/wait');
53 }
54
55 if (options.app) {
56 if (isWsl && options.app.startsWith('/mnt/')) {
57 const windowsPath = await wslToWindowsPath(options.app);
58 options.app = windowsPath;
59 }
60
61 cliArguments.push(options.app);
62 }
63
64 if (appArguments.length > 0) {
65 cliArguments.push(...appArguments);
66 }
67 } else {
68 if (options.app) {
69 command = options.app;
70 } else {
71 const useSystemXdgOpen = process.versions.electron || process.platform === 'android';
72 command = useSystemXdgOpen ? 'xdg-open' : path.join(__dirname, 'xdg-open');
73 }
74
75 if (appArguments.length > 0) {
76 cliArguments.push(...appArguments);
77 }
78
79 if (!options.wait) {
80 // `xdg-open` will block the process unless stdio is ignored
81 // and it's detached from the parent even if it's unref'd.
82 childProcessOptions.stdio = 'ignore';
83 childProcessOptions.detached = true;
84 }
85 }
86
87 cliArguments.push(target);
88
89 if (process.platform === 'darwin' && appArguments.length > 0) {
90 cliArguments.push('--args', ...appArguments);
91 }
92
93 const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
94
95 if (options.wait) {
96 return new Promise((resolve, reject) => {
97 subprocess.once('error', reject);
98
99 subprocess.once('close', exitCode => {
100 if (exitCode > 0) {
101 reject(new Error(`Exited with code ${exitCode}`));
102 return;
103 }
104
105 resolve(subprocess);
106 });
107 });
108 }
109
110 subprocess.unref();
111
112 return subprocess;
113};