diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md index 14c69493e5a..4d1e6ad3020 100644 --- a/packages/react-dev-utils/README.md +++ b/packages/react-dev-utils/README.md @@ -142,6 +142,22 @@ compiler.plugin('done', function(stats) { }); ``` +#### `getProcessForPort(port: number): string` + +Finds the currently running process on `port`. +Returns a string containing the name and directory, e.g., + +``` +create-react-app +in /Users/developer/create-react-app +``` + +```js +var getProcessForPort = require('react-dev-utils/getProcessForPort'); + +getProcessForPort(3000); +``` + #### `openBrowser(url: string): boolean` Attempts to open the browser with a given URL. diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js new file mode 100644 index 00000000000..5540fbad47a --- /dev/null +++ b/packages/react-dev-utils/getProcessForPort.js @@ -0,0 +1,61 @@ +var chalk = require('chalk'); +var execSync = require('child_process').execSync; +var path = require('path'); + +var execOptions = { + encoding: 'utf8', + stdio: [ + 'pipe', // stdin (default) + 'pipe', // stdout (default) + 'ignore' //stderr + ] +}; + +function isProcessAReactApp(processCommand) { + return /^node .*react-scripts\/scripts\/start\.js\s?$/.test(processCommand); +} + +function getProcessIdOnPort(port) { + return execSync('lsof -i:' + port + ' -P -t -sTCP:LISTEN', execOptions).trim(); +} + +function getPackageNameInDirectory(directory) { + var packagePath = path.join(directory.trim(), 'package.json'); + + try { + return require(packagePath).name; + } catch(e) { + return null; + } + +} + +function getProcessCommand(processId, processDirectory) { + var command = execSync('ps -o command -p ' + processId + ' | sed -n 2p', execOptions); + + if (isProcessAReactApp(command)) { + const packageName = getPackageNameInDirectory(processDirectory); + return (packageName) ? packageName + '\n' : command; + } else { + return command; + } + +} + +function getDirectoryOfProcessById(processId) { + return execSync('lsof -p '+ processId + ' | grep cwd | awk \'{print $9}\'', execOptions).trim(); +} + +function getProcessForPort(port) { + try { + var processId = getProcessIdOnPort(port); + var directory = getDirectoryOfProcessById(processId); + var command = getProcessCommand(processId, directory); + return chalk.cyan(command) + chalk.blue(' in ') + chalk.cyan(directory); + } catch(e) { + return null; + } +} + +module.exports = getProcessForPort; + diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 1fe5c556c79..c9f2fac2dcf 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -14,6 +14,7 @@ "clearConsole.js", "checkRequiredFiles.js", "formatWebpackMessages.js", + "getProcessForPort.js", "InterpolateHtmlPlugin.js", "openChrome.applescript", "openBrowser.js", diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index df341f6250e..35074baa884 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -26,6 +26,7 @@ var detect = require('detect-port'); var clearConsole = require('react-dev-utils/clearConsole'); var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); +var getProcessForPort = require('react-dev-utils/getProcessForPort'); var openBrowser = require('react-dev-utils/openBrowser'); var prompt = require('react-dev-utils/prompt'); var pathExists = require('path-exists'); @@ -279,9 +280,11 @@ detect(DEFAULT_PORT).then(port => { } clearConsole(); + var existingProcess = getProcessForPort(DEFAULT_PORT); var question = - chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') + - '\n\nWould you like to run the app on another port instead?'; + chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.' + + ((existingProcess) ? ' Probably:\n ' + existingProcess : '')) + + '\n\nWould you like to run the app on another port instead?'; prompt(question, true).then(shouldChangePort => { if (shouldChangePort) {