mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-04-01 12:42:50 +08:00
This fixes #302 by temporarily forking `detect-port`. Hopefully this fix will be merged into the original project, and we can refer to it again.
196 lines
5.4 KiB
JavaScript
196 lines
5.4 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
process.env.NODE_ENV = 'development';
|
|
|
|
var path = require('path');
|
|
var chalk = require('chalk');
|
|
var webpack = require('webpack');
|
|
var WebpackDevServer = require('webpack-dev-server');
|
|
var execSync = require('child_process').execSync;
|
|
var opn = require('opn');
|
|
var detect = require('./utils/detectPort');
|
|
var prompt = require('./utils/prompt');
|
|
var config = require('../config/webpack.config.dev');
|
|
|
|
// Tools like Cloud9 rely on this
|
|
var DEFAULT_PORT = process.env.PORT || 3000;
|
|
var compiler;
|
|
|
|
// TODO: hide this behind a flag and eliminate dead code on eject.
|
|
// This shouldn't be exposed to the user.
|
|
var handleCompile;
|
|
var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1);
|
|
if (isSmokeTest) {
|
|
handleCompile = function (err, stats) {
|
|
if (err || stats.hasErrors() || stats.hasWarnings()) {
|
|
process.exit(1);
|
|
} else {
|
|
process.exit(0);
|
|
}
|
|
};
|
|
}
|
|
|
|
var friendlySyntaxErrorLabel = 'Syntax error:';
|
|
|
|
function isLikelyASyntaxError(message) {
|
|
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
|
|
}
|
|
|
|
// This is a little hacky.
|
|
// It would be easier if webpack provided a rich error object.
|
|
|
|
function formatMessage(message) {
|
|
return message
|
|
// Make some common errors shorter:
|
|
.replace(
|
|
// Babel syntax error
|
|
'Module build failed: SyntaxError:',
|
|
friendlySyntaxErrorLabel
|
|
)
|
|
.replace(
|
|
// Webpack file not found error
|
|
/Module not found: Error: Cannot resolve 'file' or 'directory'/,
|
|
'Module not found:'
|
|
)
|
|
// Internal stacks are generally useless so we strip them
|
|
.replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '') // at ... ...:x:y
|
|
// Webpack loader names obscure CSS filenames
|
|
.replace('./~/css-loader!./~/postcss-loader!', '');
|
|
}
|
|
|
|
function clearConsole() {
|
|
process.stdout.write('\x1bc');
|
|
}
|
|
|
|
function setupCompiler(port) {
|
|
compiler = webpack(config, handleCompile);
|
|
|
|
compiler.plugin('invalid', function() {
|
|
clearConsole();
|
|
console.log('Compiling...');
|
|
});
|
|
|
|
compiler.plugin('done', function(stats) {
|
|
clearConsole();
|
|
var hasErrors = stats.hasErrors();
|
|
var hasWarnings = stats.hasWarnings();
|
|
if (!hasErrors && !hasWarnings) {
|
|
console.log(chalk.green('Compiled successfully!'));
|
|
console.log();
|
|
console.log('The app is running at http://localhost:' + port + '/');
|
|
console.log();
|
|
return;
|
|
}
|
|
|
|
var json = stats.toJson();
|
|
var formattedErrors = json.errors.map(message =>
|
|
'Error in ' + formatMessage(message)
|
|
);
|
|
var formattedWarnings = json.warnings.map(message =>
|
|
'Warning in ' + formatMessage(message)
|
|
);
|
|
|
|
if (hasErrors) {
|
|
console.log(chalk.red('Failed to compile.'));
|
|
console.log();
|
|
if (formattedErrors.some(isLikelyASyntaxError)) {
|
|
// If there are any syntax errors, show just them.
|
|
// This prevents a confusing ESLint parsing error
|
|
// preceding a much more useful Babel syntax error.
|
|
formattedErrors = formattedErrors.filter(isLikelyASyntaxError);
|
|
}
|
|
formattedErrors.forEach(message => {
|
|
console.log(message);
|
|
console.log();
|
|
});
|
|
// If errors exist, ignore warnings.
|
|
return;
|
|
}
|
|
|
|
if (hasWarnings) {
|
|
console.log(chalk.yellow('Compiled with warnings.'));
|
|
console.log();
|
|
formattedWarnings.forEach(message => {
|
|
console.log(message);
|
|
console.log();
|
|
});
|
|
|
|
console.log('You may use special comments to disable some warnings.');
|
|
console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.');
|
|
console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.');
|
|
}
|
|
});
|
|
}
|
|
|
|
function openBrowser(port) {
|
|
if (process.platform === 'darwin') {
|
|
try {
|
|
// Try our best to reuse existing tab
|
|
// on OS X Google Chrome with AppleScript
|
|
execSync('ps cax | grep "Google Chrome"');
|
|
execSync(
|
|
'osascript ' +
|
|
path.resolve(__dirname, './utils/chrome.applescript') +
|
|
' http://localhost:' + port + '/'
|
|
);
|
|
return;
|
|
} catch (err) {
|
|
// Ignore errors.
|
|
}
|
|
}
|
|
// Fallback to opn
|
|
// (It will always open new tab)
|
|
opn('http://localhost:' + port + '/');
|
|
}
|
|
|
|
function runDevServer(port) {
|
|
new WebpackDevServer(compiler, {
|
|
historyApiFallback: true,
|
|
hot: true, // Note: only CSS is currently hot reloaded
|
|
publicPath: config.output.publicPath,
|
|
quiet: true,
|
|
watchOptions: {
|
|
ignored: /node_modules/
|
|
}
|
|
}).listen(port, (err, result) => {
|
|
if (err) {
|
|
return console.log(err);
|
|
}
|
|
|
|
clearConsole();
|
|
console.log(chalk.cyan('Starting the development server...'));
|
|
console.log();
|
|
openBrowser(port);
|
|
});
|
|
}
|
|
|
|
function run(port) {
|
|
setupCompiler(port);
|
|
runDevServer(port);
|
|
}
|
|
|
|
detect(DEFAULT_PORT).then(port => {
|
|
if (port === DEFAULT_PORT) {
|
|
run(port);
|
|
return;
|
|
}
|
|
|
|
clearConsole();
|
|
var question =
|
|
chalk.yellow('Something is already running at port ' + DEFAULT_PORT + '.') +
|
|
'\n\nWould you like to run the app at another port instead?';
|
|
|
|
prompt(question, true).then(shouldChangePort => {
|
|
if (shouldChangePort) {
|
|
run(port);
|
|
}
|
|
});
|
|
});
|