Skip to content

Commit 7f5995e

Browse files
committedMar 20, 2023
Require Node.js 14 and move to ESM
1 parent 3b79981 commit 7f5995e

File tree

7 files changed

+210
-250
lines changed

7 files changed

+210
-250
lines changed
 

‎.github/workflows/main.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
node-version:
13+
- 18
14+
- 16
1315
- 14
14-
- 12
1516
steps:
16-
- uses: actions/checkout@v2
17-
- uses: actions/setup-node@v1
17+
- uses: actions/checkout@v3
18+
- uses: actions/setup-node@v3
1819
with:
1920
node-version: ${{ matrix.node-version }}
2021
- run: npm install

‎index.d.ts

+105-119
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,157 @@
1-
import {ChildProcess} from 'child_process';
1+
import {type ChildProcess} from 'node:child_process';
22

3-
declare namespace open {
4-
interface Options {
5-
/**
6-
Wait for the opened app to exit before fulfilling the promise. If `false` it's fulfilled immediately when opening the app.
7-
8-
Note that it waits for the app to exit, not just for the window to close.
9-
10-
On Windows, you have to explicitly specify an app for it to be able to wait.
3+
export type Options = {
4+
/**
5+
Wait for the opened app to exit before fulfilling the promise. If `false` it's fulfilled immediately when opening the app.
116
12-
@default false
13-
*/
14-
readonly wait?: boolean;
7+
Note that it waits for the app to exit, not just for the window to close.
158
16-
/**
17-
__macOS only__
9+
On Windows, you have to explicitly specify an app for it to be able to wait.
1810
19-
Do not bring the app to the foreground.
11+
@default false
12+
*/
13+
readonly wait?: boolean;
2014

21-
@default false
22-
*/
23-
readonly background?: boolean;
15+
/**
16+
__macOS only__
2417
25-
/**
26-
__macOS only__
18+
Do not bring the app to the foreground.
2719
28-
Open a new instance of the app even it's already running.
20+
@default false
21+
*/
22+
readonly background?: boolean;
2923

30-
A new instance is always opened on other platforms.
24+
/**
25+
__macOS only__
3126
32-
@default false
33-
*/
34-
readonly newInstance?: boolean;
27+
Open a new instance of the app even it's already running.
3528
36-
/**
37-
Specify the `name` of the app to open the `target` with, and optionally, app `arguments`. `app` can be an array of apps to try to open and `name` can be an array of app names to try. If each app fails, the last error will be thrown.
29+
A new instance is always opened on other platforms.
3830
39-
The app name is platform dependent. Don't hard code it in reusable modules. For example, Chrome is `google chrome` on macOS, `google-chrome` on Linux and `chrome` on Windows. If possible, use [`open.apps`](#openapps) which auto-detects the correct binary to use.
31+
@default false
32+
*/
33+
readonly newInstance?: boolean;
4034

41-
You may also pass in the app's full path. For example on WSL, this can be `/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe` for the Windows installation of Chrome.
35+
/**
36+
Specify the `name` of the app to open the `target` with, and optionally, app `arguments`. `app` can be an array of apps to try to open and `name` can be an array of app names to try. If each app fails, the last error will be thrown.
4237
43-
The app `arguments` are app dependent. Check the app's documentation for what arguments it accepts.
44-
*/
45-
readonly app?: App | readonly App[];
38+
The app name is platform dependent. Don't hard code it in reusable modules. For example, Chrome is `google chrome` on macOS, `google-chrome` on Linux and `chrome` on Windows. If possible, use `apps` which auto-detects the correct binary to use.
4639
47-
/**
48-
Allow the opened app to exit with nonzero exit code when the `wait` option is `true`.
40+
You may also pass in the app's full path. For example on WSL, this can be `/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe` for the Windows installation of Chrome.
4941
50-
We do not recommend setting this option. The convention for success is exit code zero.
42+
The app `arguments` are app dependent. Check the app's documentation for what arguments it accepts.
43+
*/
44+
readonly app?: App | readonly App[];
5145

52-
@default false
53-
*/
54-
readonly allowNonzeroExitCode?: boolean;
55-
}
46+
/**
47+
Allow the opened app to exit with nonzero exit code when the `wait` option is `true`.
5648
57-
interface OpenAppOptions extends Omit<Options, 'app'> {
58-
/**
59-
Arguments passed to the app.
49+
We do not recommend setting this option. The convention for success is exit code zero.
6050
61-
These arguments are app dependent. Check the app's documentation for what arguments it accepts.
62-
*/
63-
readonly arguments?: readonly string[];
64-
}
51+
@default false
52+
*/
53+
readonly allowNonzeroExitCode?: boolean;
54+
};
6555

66-
type AppName =
67-
| 'chrome'
68-
| 'firefox'
69-
| 'edge'
70-
| 'browser'
71-
| 'browserPrivate';
56+
export type OpenAppOptions = {
57+
/**
58+
Arguments passed to the app.
7259
73-
type App = {
74-
name: string | readonly string[];
75-
arguments?: readonly string[];
76-
};
77-
}
60+
These arguments are app dependent. Check the app's documentation for what arguments it accepts.
61+
*/
62+
readonly arguments?: readonly string[];
63+
} & Omit<Options, 'app'>;
64+
65+
export type AppName =
66+
| 'chrome'
67+
| 'firefox'
68+
| 'edge'
69+
| 'browser'
70+
| 'browserPrivate';
71+
72+
export type App = {
73+
name: string | readonly string[];
74+
arguments?: readonly string[];
75+
};
7876

7977
/**
8078
An object containing auto-detected binary names for common apps. Useful to work around cross-platform differences.
8179
8280
@example
8381
```
84-
import open from 'open';
82+
import open, {apps} from 'open';
8583
8684
await open('https://google.com', {
8785
app: {
88-
name: open.apps.chrome
86+
name: apps.chrome
8987
}
9088
});
9189
```
9290
*/
93-
declare const apps: Record<open.AppName, string | readonly string[]>;
91+
export const apps: Record<AppName, string | readonly string[]>;
9492

95-
// eslint-disable-next-line no-redeclare
96-
declare const open: {
97-
/**
98-
Open stuff like URLs, files, executables. Cross-platform.
99-
100-
Uses the command `open` on macOS, `start` on Windows and `xdg-open` on other platforms.
93+
/**
94+
Open stuff like URLs, files, executables. Cross-platform.
10195
102-
There is a caveat for [double-quotes on Windows](https://github.com/sindresorhus/open#double-quotes-on-windows) where all double-quotes are stripped from the `target`.
96+
Uses the command `open` on macOS, `start` on Windows and `xdg-open` on other platforms.
10397
104-
@param target - The thing you want to open. Can be a URL, file, or executable. Opens in the default app for the file type. For example, URLs open in your default browser.
105-
@returns The [spawned child process](https://nodejs.org/api/child_process.html#child_process_class_childprocess). You would normally not need to use this for anything, but it can be useful if you'd like to attach custom event listeners or perform other operations directly on the spawned process.
98+
There is a caveat for [double-quotes on Windows](https://github.com/sindresorhus/open#double-quotes-on-windows) where all double-quotes are stripped from the `target`.
10699
107-
@example
108-
```
109-
import open from 'open';
100+
@param target - The thing you want to open. Can be a URL, file, or executable. Opens in the default app for the file type. For example, URLs open in your default browser.
101+
@returns The [spawned child process](https://nodejs.org/api/child_process.html#child_process_class_childprocess). You would normally not need to use this for anything, but it can be useful if you'd like to attach custom event listeners or perform other operations directly on the spawned process.
110102
111-
// Opens the image in the default image viewer.
112-
await open('unicorn.png', {wait: true});
113-
console.log('The image viewer app quit');
103+
@example
104+
```
105+
import open, {apps} from 'open';
114106
115-
// Opens the URL in the default browser.
116-
await open('https://sindresorhus.com');
107+
// Opens the image in the default image viewer.
108+
await open('unicorn.png', {wait: true});
109+
console.log('The image viewer app quit');
117110
118-
// Opens the URL in a specified browser.
119-
await open('https://sindresorhus.com', {app: {name: 'firefox'}});
111+
// Opens the URL in the default browser.
112+
await open('https://sindresorhus.com');
120113
121-
// Specify app arguments.
122-
await open('https://sindresorhus.com', {app: {name: 'google chrome', arguments: ['--incognito']}});
114+
// Opens the URL in a specified browser.
115+
await open('https://sindresorhus.com', {app: {name: 'firefox'}});
123116
124-
// Opens the URL in the default browser in incognito mode.
125-
await open('https://sindresorhus.com', {app: {name: open.apps.browserPrivate}});
126-
```
127-
*/
128-
(
129-
target: string,
130-
options?: open.Options
131-
): Promise<ChildProcess>;
117+
// Specify app arguments.
118+
await open('https://sindresorhus.com', {app: {name: 'google chrome', arguments: ['--incognito']}});
132119
133-
/**
134-
An object containing auto-detected binary names for common apps. Useful to work around cross-platform differences.
135-
*/
136-
apps: typeof apps;
137-
138-
/**
139-
Open an app. Cross-platform.
120+
// Opens the URL in the default browser in incognito mode.
121+
await open('https://sindresorhus.com', {app: {name: apps.browserPrivate}});
122+
```
123+
*/
124+
export default function open(
125+
target: string,
126+
options?: Options
127+
): Promise<ChildProcess>;
140128

141-
Uses the command `open` on macOS, `start` on Windows and `xdg-open` on other platforms.
129+
/**
130+
Open an app. Cross-platform.
142131
143-
@param name - The app you want to open. Can be either builtin supported `open.apps` names or other name supported in platform.
144-
@returns The [spawned child process](https://nodejs.org/api/child_process.html#child_process_class_childprocess). You would normally not need to use this for anything, but it can be useful if you'd like to attach custom event listeners or perform other operations directly on the spawned process.
132+
Uses the command `open` on macOS, `start` on Windows and `xdg-open` on other platforms.
145133
146-
@example
147-
```
148-
import open from 'open';
149-
const {apps, openApp} = open;
134+
@param name - The app you want to open. Can be either builtin supported `apps` names or other name supported in platform.
135+
@returns The [spawned child process](https://nodejs.org/api/child_process.html#child_process_class_childprocess). You would normally not need to use this for anything, but it can be useful if you'd like to attach custom event listeners or perform other operations directly on the spawned process.
150136
151-
// Open Firefox.
152-
await openApp(apps.firefox);
137+
@example
138+
```
139+
import open, {openApp, apps} from 'open';
153140
154-
// Open Chrome in incognito mode.
155-
await openApp(apps.chrome, {arguments: ['--incognito']});
141+
// Open Firefox.
142+
await openApp(apps.firefox);
156143
157-
// Open default browser.
158-
await openApp(apps.browser);
144+
// Open Chrome in incognito mode.
145+
await openApp(apps.chrome, {arguments: ['--incognito']});
159146
160-
// Open default browser in incognito mode.
161-
await openApp(apps.browserPrivate);
147+
// Open default browser.
148+
await openApp(apps.browser);
162149
163-
// Open Xcode.
164-
await openApp('xcode');
165-
```
166-
*/
167-
openApp: (name: open.App['name'], options?: open.OpenAppOptions) => Promise<ChildProcess>;
168-
};
150+
// Open default browser in incognito mode.
151+
await openApp(apps.browserPrivate);
169152
170-
export {apps};
171-
export default open;
153+
// Open Xcode.
154+
await openApp('xcode');
155+
```
156+
*/
157+
export function openApp(name: App['name'], options?: OpenAppOptions): Promise<ChildProcess>;

‎index.js

+38-58
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,21 @@
1-
import path from 'path';
2-
import {fileURLToPath} from 'url';
3-
import childProcess from 'child_process';
4-
import {promises as fs, constants as fsConstants} from 'fs';
1+
import process from 'node:process';
2+
import {Buffer} from 'node:buffer';
3+
import path from 'node:path';
4+
import {fileURLToPath} from 'node:url';
5+
import childProcess from 'node:child_process';
6+
import fs from 'node:fs/promises';
7+
import {constants as fsConstants} from 'node:fs'; // TODO: Move this to the above import when targeting Node.js 18.
58
import isWsl from 'is-wsl';
6-
import isDocker from 'is-docker';
79
import defineLazyProperty from 'define-lazy-prop';
810
import defaultBrowser from 'default-browser';
11+
import isInsideContainer from 'is-inside-container';
912

1013
// Path to included `xdg-open`.
1114
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1215
const localXdgOpenPath = path.join(__dirname, 'xdg-open');
1316

1417
const {platform, arch} = process;
1518

16-
// Podman detection
17-
const hasContainerEnv = () => {
18-
try {
19-
fs.statSync('/run/.containerenv');
20-
return true;
21-
} catch {
22-
return false;
23-
}
24-
};
25-
26-
let cachedResult;
27-
function isInsideContainer() {
28-
if (cachedResult === undefined) {
29-
cachedResult = hasContainerEnv() || isDocker();
30-
}
31-
32-
return cachedResult;
33-
}
34-
3519
/**
3620
Get the mount point for fixed drives in WSL.
3721
@@ -97,26 +81,26 @@ const baseOpen = async options => {
9781
background: false,
9882
newInstance: false,
9983
allowNonzeroExitCode: false,
100-
...options
84+
...options,
10185
};
10286

10387
if (Array.isArray(options.app)) {
10488
return pTryEach(options.app, singleApp => baseOpen({
10589
...options,
106-
app: singleApp
90+
app: singleApp,
10791
}));
10892
}
10993

110-
let {name: app, arguments: appArguments = []} = options.app || {};
94+
let {name: app, arguments: appArguments = []} = options.app ?? {};
11195
appArguments = [...appArguments];
11296

11397
if (Array.isArray(app)) {
11498
return pTryEach(app, appName => baseOpen({
11599
...options,
116100
app: {
117101
name: appName,
118-
arguments: appArguments
119-
}
102+
arguments: appArguments,
103+
},
120104
}));
121105
}
122106

@@ -129,14 +113,14 @@ const baseOpen = async options => {
129113
'firefox.desktop': 'firefox',
130114
'com.microsoft.msedge': 'edge',
131115
'com.microsoft.edge': 'edge',
132-
'microsoft-edge.desktop': 'edge'
116+
'microsoft-edge.desktop': 'edge',
133117
};
134118

135-
// Incognito flags for each browser in open.apps
119+
// Incognito flags for each browser in `apps`.
136120
const flags = {
137121
chrome: '--incognito',
138122
firefox: '--private-window',
139-
edge: '--inPrivate'
123+
edge: '--inPrivate',
140124
};
141125

142126
const browser = await defaultBrowser();
@@ -150,9 +134,9 @@ const baseOpen = async options => {
150134
return baseOpen({
151135
...options,
152136
app: {
153-
name: open.apps[browserName],
154-
arguments: appArguments
155-
}
137+
name: apps[browserName],
138+
arguments: appArguments,
139+
},
156140
});
157141
}
158142

@@ -184,16 +168,16 @@ const baseOpen = async options => {
184168
} else if (platform === 'win32' || (isWsl && !isInsideContainer() && !app)) {
185169
const mountPoint = await getWslDrivesMountPoint();
186170

187-
command = isWsl ?
188-
`${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` :
189-
`${process.env.SYSTEMROOT}\\System32\\WindowsPowerShell\\v1.0\\powershell`;
171+
command = isWsl
172+
? `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`
173+
: `${process.env.SYSTEMROOT}\\System32\\WindowsPowerShell\\v1.0\\powershell`;
190174

191175
cliArguments.push(
192176
'-NoProfile',
193177
'-NonInteractive',
194178
'-ExecutionPolicy',
195179
'Bypass',
196-
'-EncodedCommand'
180+
'-EncodedCommand',
197181
);
198182

199183
if (!isWsl) {
@@ -238,8 +222,8 @@ const baseOpen = async options => {
238222
exeLocalXdgOpen = true;
239223
} catch {}
240224

241-
const useSystemXdgOpen = process.versions.electron ||
242-
platform === 'android' || isBundled || !exeLocalXdgOpen;
225+
const useSystemXdgOpen = process.versions.electron
226+
?? (platform === 'android' || isBundled || !exeLocalXdgOpen);
243227
command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;
244228
}
245229

@@ -292,16 +276,16 @@ const open = (target, options) => {
292276

293277
return baseOpen({
294278
...options,
295-
target
279+
target,
296280
});
297281
};
298282

299-
const openApp = (name, options) => {
283+
export const openApp = (name, options) => {
300284
if (typeof name !== 'string') {
301285
throw new TypeError('Expected a `name`');
302286
}
303287

304-
const {arguments: appArguments = []} = options || {};
288+
const {arguments: appArguments = []} = options ?? {};
305289
if (appArguments !== undefined && appArguments !== null && !Array.isArray(appArguments)) {
306290
throw new TypeError('Expected `appArguments` as Array type');
307291
}
@@ -310,8 +294,8 @@ const openApp = (name, options) => {
310294
...options,
311295
app: {
312296
name,
313-
arguments: appArguments
314-
}
297+
arguments: appArguments,
298+
},
315299
});
316300
};
317301

@@ -341,41 +325,37 @@ function detectPlatformBinary({[platform]: platformBinary}, {wsl}) {
341325
return detectArchBinary(platformBinary);
342326
}
343327

344-
const apps = {};
328+
export const apps = {};
345329

346330
defineLazyProperty(apps, 'chrome', () => detectPlatformBinary({
347331
darwin: 'google chrome',
348332
win32: 'chrome',
349-
linux: ['google-chrome', 'google-chrome-stable', 'chromium']
333+
linux: ['google-chrome', 'google-chrome-stable', 'chromium'],
350334
}, {
351335
wsl: {
352336
ia32: '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe',
353-
x64: ['/mnt/c/Program Files/Google/Chrome/Application/chrome.exe', '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe']
354-
}
337+
x64: ['/mnt/c/Program Files/Google/Chrome/Application/chrome.exe', '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'],
338+
},
355339
}));
356340

357341
defineLazyProperty(apps, 'firefox', () => detectPlatformBinary({
358342
darwin: 'firefox',
359343
win32: 'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
360-
linux: 'firefox'
344+
linux: 'firefox',
361345
}, {
362-
wsl: '/mnt/c/Program Files/Mozilla Firefox/firefox.exe'
346+
wsl: '/mnt/c/Program Files/Mozilla Firefox/firefox.exe',
363347
}));
364348

365349
defineLazyProperty(apps, 'edge', () => detectPlatformBinary({
366350
darwin: 'microsoft edge',
367351
win32: 'msedge',
368-
linux: ['microsoft-edge', 'microsoft-edge-dev']
352+
linux: ['microsoft-edge', 'microsoft-edge-dev'],
369353
}, {
370-
wsl: '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
354+
wsl: '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
371355
}));
372356

373357
defineLazyProperty(apps, 'browser', () => 'browser');
374358

375359
defineLazyProperty(apps, 'browserPrivate', () => 'browserPrivate');
376360

377-
open.apps = apps;
378-
open.openApp = openApp;
379-
380-
export {apps};
381361
export default open;

‎index.test-d.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1+
import {type ChildProcess} from 'node:child_process';
12
import {expectType} from 'tsd';
2-
import {ChildProcess} from 'child_process';
3-
import open from '.';
4-
5-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6-
const options: open.Options = {};
3+
import open from './index.js';
74

85
expectType<Promise<ChildProcess>>(open('foo'));
96
expectType<Promise<ChildProcess>>(open('foo', {app: {
10-
name: 'bar'
7+
name: 'bar',
118
}}));
129
expectType<Promise<ChildProcess>>(open('foo', {app: {
1310
name: 'bar',
14-
arguments: ['--arg']
11+
arguments: ['--arg'],
1512
}}));
1613
expectType<Promise<ChildProcess>>(open('foo', {wait: true}));
1714
expectType<Promise<ChildProcess>>(open('foo', {background: true}));

‎package.json

+14-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"name": "open",
33
"version": "8.4.0",
4-
"type": "module",
54
"description": "Open stuff like URLs, files, executables. Cross-platform.",
65
"license": "MIT",
76
"repository": "sindresorhus/open",
@@ -11,8 +10,13 @@
1110
"email": "sindresorhus@gmail.com",
1211
"url": "https://sindresorhus.com"
1312
},
13+
"type": "module",
14+
"exports": {
15+
"types": "./index.d.ts",
16+
"default": "./index.js"
17+
},
1418
"engines": {
15-
"node": ">=12"
19+
"node": ">=14.16"
1620
},
1721
"scripts": {
1822
"test": "xo && tsd"
@@ -49,15 +53,15 @@
4953
"file"
5054
],
5155
"dependencies": {
52-
"define-lazy-prop": "^2.0.0",
53-
"is-docker": "^2.1.1",
54-
"is-wsl": "^2.2.0",
55-
"default-browser": "^3.1.0"
56+
"default-browser": "^3.1.0",
57+
"define-lazy-prop": "^3.0.0",
58+
"is-inside-container": "^1.0.0",
59+
"is-wsl": "^2.2.0"
5660
},
5761
"devDependencies": {
58-
"@types/node": "^15.0.0",
59-
"ava": "^3.15.0",
60-
"tsd": "^0.14.0",
61-
"xo": "^0.39.1"
62+
"@types/node": "^18.15.3",
63+
"ava": "^5.2.0",
64+
"tsd": "^0.28.0",
65+
"xo": "^0.53.1"
6266
}
6367
}

‎readme.md

+35-42
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ This package does not make any security guarantees. If you pass in untrusted inp
2323
npm install open
2424
```
2525

26+
**Warning:** This package is native [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and no longer provides a CommonJS export. If your project uses CommonJS, you will have to [convert to ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) or use the [dynamic `import()`](https://v8.dev/features/dynamic-import) function. Please don't open issues for questions regarding CommonJS / ESM.
27+
2628
## Usage
2729

2830
```js
29-
import open from 'open';
31+
import open, {openApp, apps} from 'open';
3032

3133
// Opens the image in the default image viewer and waits for the opened app to quit.
3234
await open('unicorn.png', {wait: true});
@@ -42,13 +44,13 @@ await open('https://sindresorhus.com', {app: {name: 'firefox'}});
4244
await open('https://sindresorhus.com', {app: {name: 'google chrome', arguments: ['--incognito']}});
4345

4446
// Opens the URL in the default browser in incognito mode.
45-
await open('https://sindresorhus.com', {app: {name: open.apps.browserPrivate}});
47+
await open('https://sindresorhus.com', {app: {name: apps.browserPrivate}});
4648

4749
// Open an app.
48-
await open.openApp('xcode');
50+
await openApp('xcode');
4951

5052
// Open an app with arguments.
51-
await open.openApp(open.apps.chrome, {arguments: ['--incognito']});
53+
await openApp(apps.chrome, {arguments: ['--incognito']});
5254
```
5355

5456
## API
@@ -104,7 +106,7 @@ Type: `{name: string | string[], arguments?: string[]} | Array<{name: string | s
104106

105107
Specify the `name` of the app to open the `target` with, and optionally, app `arguments`. `app` can be an array of apps to try to open and `name` can be an array of app names to try. If each app fails, the last error will be thrown.
106108

107-
The app name is platform dependent. Don't hard code it in reusable modules. For example, Chrome is `google chrome` on macOS, `google-chrome` on Linux and `chrome` on Windows. If possible, use [`open.apps`](#openapps) which auto-detects the correct binary to use.
109+
The app name is platform dependent. Don't hard code it in reusable modules. For example, Chrome is `google chrome` on macOS, `google-chrome` on Linux and `chrome` on Windows. If possible, use [`apps`](#apps) which auto-detects the correct binary to use.
108110

109111
You may also pass in the app's full path. For example on WSL, this can be `/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe` for the Windows installation of Chrome.
110112

@@ -119,42 +121,7 @@ Allow the opened app to exit with nonzero exit code when the `wait` option is `t
119121

120122
We do not recommend setting this option. The convention for success is exit code zero.
121123

122-
### open.apps / apps
123-
124-
An object containing auto-detected binary names for common apps. Useful to work around [cross-platform differences](#app).
125-
126-
```js
127-
// Using default export.
128-
import open from 'open';
129-
130-
await open('https://google.com', {
131-
app: {
132-
name: open.apps.chrome
133-
}
134-
});
135-
136-
// Using named export.
137-
import open, {apps} from 'open';
138-
139-
await open('https://firefox.com', {
140-
app: {
141-
name: apps.browserPrivate
142-
}
143-
});
144-
```
145-
`browser` and `browserPrivate` can also be used to access the user's default browser through [`default-browser`](https://github.com/sindresorhus/default-browser).
146-
147-
#### Supported apps
148-
149-
- [`chrome`](https://www.google.com/chrome) - Web browser
150-
- [`firefox`](https://www.mozilla.org/firefox) - Web browser
151-
- [`edge`](https://www.microsoft.com/edge) - Web browser
152-
- `browser` - Default web browser
153-
- `browserPrivate` - Default web browser in incognito mode
154-
155-
`browser` and `browserPrivate` only supports `chrome`, `firefox` and `edge`.
156-
157-
### open.openApp(name, options?)
124+
### openApp(name, options?)
158125

159126
Open an app.
160127

@@ -164,7 +131,7 @@ Returns a promise for the [spawned child process](https://nodejs.org/api/child_p
164131

165132
Type: `string`
166133

167-
The app name is platform dependent. Don't hard code it in reusable modules. For example, Chrome is `google chrome` on macOS, `google-chrome` on Linux and `chrome` on Windows. If possible, use [`open.apps`](#openapps) which auto-detects the correct binary to use.
134+
The app name is platform dependent. Don't hard code it in reusable modules. For example, Chrome is `google chrome` on macOS, `google-chrome` on Linux and `chrome` on Windows. If possible, use [`apps`](#apps) which auto-detects the correct binary to use.
168135

169136
You may also pass in the app's full path. For example on WSL, this can be `/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe` for the Windows installation of Chrome.
170137

@@ -183,6 +150,32 @@ Arguments passed to the app.
183150

184151
These arguments are app dependent. Check the app's documentation for what arguments it accepts.
185152

153+
### apps
154+
155+
An object containing auto-detected binary names for common apps. Useful to work around [cross-platform differences](#app).
156+
157+
```js
158+
import open, {apps} from 'open';
159+
160+
await open('https://google.com', {
161+
app: {
162+
name: apps.chrome
163+
}
164+
});
165+
```
166+
167+
`browser` and `browserPrivate` can also be used to access the user's default browser through [`default-browser`](https://github.com/sindresorhus/default-browser).
168+
169+
#### Supported apps
170+
171+
- [`chrome`](https://www.google.com/chrome) - Web browser
172+
- [`firefox`](https://www.mozilla.org/firefox) - Web browser
173+
- [`edge`](https://www.microsoft.com/edge) - Web browser
174+
- `browser` - Default web browser
175+
- `browserPrivate` - Default web browser in incognito mode
176+
177+
`browser` and `browserPrivate` only supports `chrome`, `firefox`, and `edge`.
178+
186179
## Related
187180

188181
- [open-cli](https://github.com/sindresorhus/open-cli) - CLI for this module

‎test.js

+10-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import test from 'ava';
2-
import open from './index.js';
3-
const {openApp} = open;
2+
import open, {openApp, apps} from './index.js';
43

54
// Tests only checks that opening doesn't return an error
65
// it has no way make sure that it actually opened anything.
@@ -24,13 +23,13 @@ test('open URL in default app', async t => {
2423
});
2524

2625
test('open URL in specified app', async t => {
27-
await t.notThrowsAsync(open('https://sindresorhus.com', {app: {name: open.apps.chrome}}));
26+
await t.notThrowsAsync(open('https://sindresorhus.com', {app: {name: apps.chrome}}));
2827
});
2928

3029
test('open URL in specified app with arguments', async t => {
3130
await t.notThrowsAsync(async () => {
32-
const proc = await open('https://sindresorhus.com', {app: {name: open.apps.chrome, arguments: ['--incognito']}});
33-
t.deepEqual(proc.spawnargs, ['open', '-a', open.apps.chrome, 'https://sindresorhus.com', '--args', '--incognito']);
31+
const proc = await open('https://sindresorhus.com', {app: {name: apps.chrome, arguments: ['--incognito']}});
32+
t.deepEqual(proc.spawnargs, ['open', '-a', apps.chrome, 'https://sindresorhus.com', '--args', '--incognito']);
3433
});
3534
});
3635

@@ -72,25 +71,25 @@ test('open URL with query strings and URL reserved characters with `url` option'
7271
});
7372

7473
test('open Firefox without arguments', async t => {
75-
await t.notThrowsAsync(openApp(open.apps.firefox));
74+
await t.notThrowsAsync(openApp(apps.firefox));
7675
});
7776

7877
test('open Chrome in incognito mode', async t => {
79-
await t.notThrowsAsync(openApp(open.apps.chrome, {arguments: ['--incognito'], newInstance: true}));
78+
await t.notThrowsAsync(openApp(apps.chrome, {arguments: ['--incognito'], newInstance: true}));
8079
});
8180

8281
test('open URL with default browser argument', async t => {
83-
await t.notThrowsAsync(open('https://sindresorhus.com', {app: {name: open.apps.browser}}));
82+
await t.notThrowsAsync(open('https://sindresorhus.com', {app: {name: apps.browser}}));
8483
});
8584

8685
test('open URL with default browser in incognito mode', async t => {
87-
await t.notThrowsAsync(open('https://sindresorhus.com', {app: {name: open.apps.browserPrivate}}));
86+
await t.notThrowsAsync(open('https://sindresorhus.com', {app: {name: apps.browserPrivate}}));
8887
});
8988

9089
test('open default browser', async t => {
91-
await t.notThrowsAsync(openApp(open.apps.browser, {newInstance: true}));
90+
await t.notThrowsAsync(openApp(apps.browser, {newInstance: true}));
9291
});
9392

9493
test('open default browser in incognito mode', async t => {
95-
await t.notThrowsAsync(openApp(open.apps.browserPrivate, {newInstance: true}));
94+
await t.notThrowsAsync(openApp(apps.browserPrivate, {newInstance: true}));
9695
});

0 commit comments

Comments
 (0)
Please sign in to comment.