UNPKG

3.64 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8'use strict';
9
10var chalk = require('chalk');
11var execSync = require('child_process').execSync;
12var spawn = require('cross-spawn');
13var opn = require('opn');
14
15// https://github.com/sindresorhus/opn#app
16var OSX_CHROME = 'google chrome';
17
18const Actions = Object.freeze({
19 NONE: 0,
20 BROWSER: 1,
21 SCRIPT: 2,
22});
23
24function getBrowserEnv() {
25 // Attempt to honor this environment variable.
26 // It is specific to the operating system.
27 // See https://github.com/sindresorhus/opn#app for documentation.
28 const value = process.env.BROWSER;
29 let action;
30 if (!value) {
31 // Default.
32 action = Actions.BROWSER;
33 } else if (value.toLowerCase().endsWith('.js')) {
34 action = Actions.SCRIPT;
35 } else if (value.toLowerCase() === 'none') {
36 action = Actions.NONE;
37 } else {
38 action = Actions.BROWSER;
39 }
40 return { action, value };
41}
42
43function executeNodeScript(scriptPath, url) {
44 const extraArgs = process.argv.slice(2);
45 const child = spawn('node', [scriptPath, ...extraArgs, url], {
46 stdio: 'inherit',
47 });
48 child.on('close', code => {
49 if (code !== 0) {
50 console.log();
51 console.log(
52 chalk.red(
53 'The script specified as BROWSER environment variable failed.'
54 )
55 );
56 console.log(chalk.cyan(scriptPath) + ' exited with code ' + code + '.');
57 console.log();
58 return;
59 }
60 });
61 return true;
62}
63
64function startBrowserProcess(browser, url) {
65 // If we're on OS X, the user hasn't specifically
66 // requested a different browser, we can try opening
67 // Chrome with AppleScript. This lets us reuse an
68 // existing tab when possible instead of creating a new one.
69 const shouldTryOpenChromeWithAppleScript =
70 process.platform === 'darwin' &&
71 (typeof browser !== 'string' || browser === OSX_CHROME);
72
73 if (shouldTryOpenChromeWithAppleScript) {
74 try {
75 // Try our best to reuse existing tab
76 // on OS X Google Chrome with AppleScript
77 execSync('ps cax | grep "Google Chrome"');
78 execSync('osascript openChrome.applescript "' + encodeURI(url) + '"', {
79 cwd: __dirname,
80 stdio: 'ignore',
81 });
82 return true;
83 } catch (err) {
84 // Ignore errors.
85 }
86 }
87
88 // Another special case: on OS X, check if BROWSER has been set to "open".
89 // In this case, instead of passing `open` to `opn` (which won't work),
90 // just ignore it (thus ensuring the intended behavior, i.e. opening the system browser):
91 // https://github.com/facebookincubator/create-react-app/pull/1690#issuecomment-283518768
92 if (process.platform === 'darwin' && browser === 'open') {
93 browser = undefined;
94 }
95
96 // Fallback to opn
97 // (It will always open new tab)
98 try {
99 var options = { app: browser };
100 opn(url, options).catch(() => {}); // Prevent `unhandledRejection` error.
101 return true;
102 } catch (err) {
103 return false;
104 }
105}
106
107/**
108 * Reads the BROWSER evironment variable and decides what to do with it. Returns
109 * true if it opened a browser or ran a node.js script, otherwise false.
110 */
111function openBrowser(url) {
112 const { action, value } = getBrowserEnv();
113 switch (action) {
114 case Actions.NONE:
115 // Special case: BROWSER="none" will prevent opening completely.
116 return false;
117 case Actions.SCRIPT:
118 return executeNodeScript(value, url);
119 case Actions.BROWSER:
120 return startBrowserProcess(value, url);
121 default:
122 throw new Error('Not implemented.');
123 }
124}
125
126module.exports = openBrowser;