UNPKG

4.42 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 open = require('open');
14
15// https://github.com/sindresorhus/open#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/open#app for documentation.
28 const value = process.env.BROWSER;
29 const args = process.env.BROWSER_ARGS
30 ? process.env.BROWSER_ARGS.split(' ')
31 : [];
32 let action;
33 if (!value) {
34 // Default.
35 action = Actions.BROWSER;
36 } else if (value.toLowerCase().endsWith('.js')) {
37 action = Actions.SCRIPT;
38 } else if (value.toLowerCase() === 'none') {
39 action = Actions.NONE;
40 } else {
41 action = Actions.BROWSER;
42 }
43 return { action, value, args };
44}
45
46function executeNodeScript(scriptPath, url) {
47 const extraArgs = process.argv.slice(2);
48 const child = spawn(process.execPath, [scriptPath, ...extraArgs, url], {
49 stdio: 'inherit',
50 });
51 child.on('close', code => {
52 if (code !== 0) {
53 console.log();
54 console.log(
55 chalk.red(
56 'The script specified as BROWSER environment variable failed.'
57 )
58 );
59 console.log(chalk.cyan(scriptPath) + ' exited with code ' + code + '.');
60 console.log();
61 return;
62 }
63 });
64 return true;
65}
66
67function startBrowserProcess(browser, url, args) {
68 // If we're on OS X, the user hasn't specifically
69 // requested a different browser, we can try opening
70 // Chrome with AppleScript. This lets us reuse an
71 // existing tab when possible instead of creating a new one.
72 const shouldTryOpenChromiumWithAppleScript =
73 process.platform === 'darwin' &&
74 (typeof browser !== 'string' || browser === OSX_CHROME);
75
76 if (shouldTryOpenChromiumWithAppleScript) {
77 // Will use the first open browser found from list
78 const supportedChromiumBrowsers = [
79 'Google Chrome Canary',
80 'Google Chrome',
81 'Microsoft Edge',
82 'Brave Browser',
83 'Vivaldi',
84 'Chromium',
85 ];
86
87 for (let chromiumBrowser of supportedChromiumBrowsers) {
88 try {
89 // Try our best to reuse existing tab
90 // on OSX Chromium-based browser with AppleScript
91 execSync('ps cax | grep "' + chromiumBrowser + '"');
92 execSync(
93 'osascript openChrome.applescript "' +
94 encodeURI(url) +
95 '" "' +
96 chromiumBrowser +
97 '"',
98 {
99 cwd: __dirname,
100 stdio: 'ignore',
101 }
102 );
103 return true;
104 } catch (err) {
105 // Ignore errors.
106 }
107 }
108 }
109
110 // Another special case: on OS X, check if BROWSER has been set to "open".
111 // In this case, instead of passing `open` to `opn` (which won't work),
112 // just ignore it (thus ensuring the intended behavior, i.e. opening the system browser):
113 // https://github.com/facebook/create-react-app/pull/1690#issuecomment-283518768
114 if (process.platform === 'darwin' && browser === 'open') {
115 browser = undefined;
116 }
117
118 // If there are arguments, they must be passed as array with the browser
119 if (typeof browser === 'string' && args.length > 0) {
120 browser = [browser].concat(args);
121 }
122
123 // Fallback to open
124 // (It will always open new tab)
125 try {
126 var options = { app: browser, wait: false, url: true };
127 open(url, options).catch(() => {}); // Prevent `unhandledRejection` error.
128 return true;
129 } catch (err) {
130 return false;
131 }
132}
133
134/**
135 * Reads the BROWSER environment variable and decides what to do with it. Returns
136 * true if it opened a browser or ran a node.js script, otherwise false.
137 */
138function openBrowser(url) {
139 const { action, value, args } = getBrowserEnv();
140 switch (action) {
141 case Actions.NONE:
142 // Special case: BROWSER="none" will prevent opening completely.
143 return false;
144 case Actions.SCRIPT:
145 return executeNodeScript(value, url);
146 case Actions.BROWSER:
147 return startBrowserProcess(value, url, args);
148 default:
149 throw new Error('Not implemented.');
150 }
151}
152
153module.exports = openBrowser;