Skip to content

Commit 0d67d1a

Browse files
ianmcnallyrandycoulman
authored andcommitted
add logging of existing default port process on start (facebook#816)
* add logging of existing port process on start * Move port process wording in start command on to next line * Color the named processes as cyan in terminal output * Add handling for multiple processes on a part - With the currently process filtering, if multiple processes are returned as running on port 3000, this command would fail. This splits apart the process IDing and the process naming, to support multiple processes. - One curious thing about the bash command to get processes, is that it'll include browsers with a window open on localhost:3000. May want to reconsider that. * Add process directory to existing port warning - also moved terminal coloring up, when getting the process, to be able to distinguish the process command from the directory * Change output color to all cyan, except "in" * Rename getProcessNameOnPort -> getProcessForPort - better reflects its broadened scope (both command and directory) * Add checking if process is a CRA instance, to customize port running message - moved from using package.json to a regex, for reliability * Move getProcessForPort to react-dev-utils - also allowed for breakdown of commands into helper methods * Add documentation for getProcessForPort * Add getProcessForPort to list of dev-scripts files * Use app's package name when CRA app is running on another port * Filter port process by those listening - Removed the handling of multiple process IDs since you can filtering by listening process (and not have the browser in the list of processes) - Trimmed the terminal outputs for better matching (process id) and better terminal output (directory of process) * Update README on port helpers, to specify only one port returned * Add ignore of stderr when executing process commands - Make sure any potential errors don't leak to the user
1 parent fae1b21 commit 0d67d1a

File tree

4 files changed

+83
-2
lines changed

4 files changed

+83
-2
lines changed

packages/react-dev-utils/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,22 @@ compiler.plugin('done', function(stats) {
142142
});
143143
```
144144

145+
#### `getProcessForPort(port: number): string`
146+
147+
Finds the currently running process on `port`.
148+
Returns a string containing the name and directory, e.g.,
149+
150+
```
151+
create-react-app
152+
in /Users/developer/create-react-app
153+
```
154+
155+
```js
156+
var getProcessForPort = require('react-dev-utils/getProcessForPort');
157+
158+
getProcessForPort(3000);
159+
```
160+
145161
#### `openBrowser(url: string): boolean`
146162

147163
Attempts to open the browser with a given URL.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
var chalk = require('chalk');
2+
var execSync = require('child_process').execSync;
3+
var path = require('path');
4+
5+
var execOptions = {
6+
encoding: 'utf8',
7+
stdio: [
8+
'pipe', // stdin (default)
9+
'pipe', // stdout (default)
10+
'ignore' //stderr
11+
]
12+
};
13+
14+
function isProcessAReactApp(processCommand) {
15+
return /^node .*react-scripts\/scripts\/start\.js\s?$/.test(processCommand);
16+
}
17+
18+
function getProcessIdOnPort(port) {
19+
return execSync('lsof -i:' + port + ' -P -t -sTCP:LISTEN', execOptions).trim();
20+
}
21+
22+
function getPackageNameInDirectory(directory) {
23+
var packagePath = path.join(directory.trim(), 'package.json');
24+
25+
try {
26+
return require(packagePath).name;
27+
} catch(e) {
28+
return null;
29+
}
30+
31+
}
32+
33+
function getProcessCommand(processId, processDirectory) {
34+
var command = execSync('ps -o command -p ' + processId + ' | sed -n 2p', execOptions);
35+
36+
if (isProcessAReactApp(command)) {
37+
const packageName = getPackageNameInDirectory(processDirectory);
38+
return (packageName) ? packageName + '\n' : command;
39+
} else {
40+
return command;
41+
}
42+
43+
}
44+
45+
function getDirectoryOfProcessById(processId) {
46+
return execSync('lsof -p '+ processId + ' | grep cwd | awk \'{print $9}\'', execOptions).trim();
47+
}
48+
49+
function getProcessForPort(port) {
50+
try {
51+
var processId = getProcessIdOnPort(port);
52+
var directory = getDirectoryOfProcessById(processId);
53+
var command = getProcessCommand(processId, directory);
54+
return chalk.cyan(command) + chalk.blue(' in ') + chalk.cyan(directory);
55+
} catch(e) {
56+
return null;
57+
}
58+
}
59+
60+
module.exports = getProcessForPort;
61+

packages/react-dev-utils/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"clearConsole.js",
1515
"checkRequiredFiles.js",
1616
"formatWebpackMessages.js",
17+
"getProcessForPort.js",
1718
"InterpolateHtmlPlugin.js",
1819
"openChrome.applescript",
1920
"openBrowser.js",

packages/react-scripts/scripts/start.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var detect = require('detect-port');
2626
var clearConsole = require('react-dev-utils/clearConsole');
2727
var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
2828
var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
29+
var getProcessForPort = require('react-dev-utils/getProcessForPort');
2930
var openBrowser = require('react-dev-utils/openBrowser');
3031
var prompt = require('react-dev-utils/prompt');
3132
var pathExists = require('path-exists');
@@ -279,9 +280,11 @@ detect(DEFAULT_PORT).then(port => {
279280
}
280281

281282
clearConsole();
283+
var existingProcess = getProcessForPort(DEFAULT_PORT);
282284
var question =
283-
chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') +
284-
'\n\nWould you like to run the app on another port instead?';
285+
chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.' +
286+
((existingProcess) ? ' Probably:\n ' + existingProcess : '')) +
287+
'\n\nWould you like to run the app on another port instead?';
285288

286289
prompt(question, true).then(shouldChangePort => {
287290
if (shouldChangePort) {

0 commit comments

Comments
 (0)