mirror of
https://github.com/zhigang1992/create-react-app.git
synced 2026-06-15 09:58:21 +08:00
225 lines
7.7 KiB
JavaScript
225 lines
7.7 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('detect-port');
|
|
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);
|
|
}
|
|
};
|
|
}
|
|
|
|
// Some custom utilities to prettify Webpack output.
|
|
// This is a little hacky.
|
|
// It would be easier if webpack provided a rich error object.
|
|
var friendlySyntaxErrorLabel = 'Syntax error:';
|
|
function isLikelyASyntaxError(message) {
|
|
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
|
|
}
|
|
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() {
|
|
// This seems to work best on Windows and other systems.
|
|
// The intention is to clear the output so you can focus on most recent build.
|
|
process.stdout.write('\x1bc');
|
|
}
|
|
|
|
function setupCompiler(port) {
|
|
// "Compiler" is a low-level interface to Webpack.
|
|
// It lets us listen to some events and provide our own custom messages.
|
|
compiler = webpack(config, handleCompile);
|
|
|
|
// "invalid" event fires when you have changed a file, and Webpack is
|
|
// recompiling a bundle. WebpackDevServer takes care to pause serving the
|
|
// bundle, so if you refresh, it'll wait instead of serving the old one.
|
|
// "invalid" is short for "bundle invalidated", it doesn't imply any errors.
|
|
compiler.plugin('invalid', function() {
|
|
clearConsole();
|
|
console.log('Compiling...');
|
|
});
|
|
|
|
// "done" event fires when Webpack has finished recompiling the bundle.
|
|
// Whether or not you have warnings or errors, you will get this event.
|
|
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:');
|
|
console.log();
|
|
console.log(' ' + chalk.cyan('http://localhost:' + port + '/'));
|
|
console.log();
|
|
console.log('Note that the development build is not optimized.');
|
|
console.log('To create a production build, use ' + chalk.cyan('npm run build') + '.');
|
|
console.log();
|
|
return;
|
|
}
|
|
|
|
// We have switched off the default Webpack output in WebpackDevServer
|
|
// options so we are going to "massage" the warnings and errors and present
|
|
// them in a readable focused way.
|
|
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();
|
|
});
|
|
// Teach some ESLint tricks.
|
|
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 chrome.applescript http://localhost:' + port + '/',
|
|
{cwd: path.join(__dirname, 'utils'), stdio: 'ignore'}
|
|
);
|
|
return;
|
|
} catch (err) {
|
|
// Ignore errors.
|
|
}
|
|
}
|
|
// Fallback to opn
|
|
// (It will always open new tab)
|
|
opn('http://localhost:' + port + '/');
|
|
}
|
|
|
|
function runDevServer(port) {
|
|
// Launch WebpackDevServer.
|
|
new WebpackDevServer(compiler, {
|
|
// When an unrecognized URL is requested (e.g. localhost:3000/todos),
|
|
// assume that this is a single-page app, and serve index.html.
|
|
historyApiFallback: true,
|
|
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
|
// for the WebpackDevServer client so it can learn when the files were
|
|
// updated. The WebpackDevServer client is included as an entry point
|
|
// in the Webpack development configuration. Note that only changes
|
|
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
|
hot: true,
|
|
// It is important to tell WebpackDevServer to use the same "root" path
|
|
// as we specified in the config. In development, we always serve from /.
|
|
publicPath: config.output.publicPath,
|
|
// WebpackDevServer is noisy by default so we emit custom message instead
|
|
// by listening to the compiler events with `compiler.plugin` calls above.
|
|
quiet: true,
|
|
// Reportedly, this avoids CPU overload on some systems.
|
|
// https://github.com/facebookincubator/create-react-app/issues/293
|
|
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);
|
|
}
|
|
|
|
// We attempt to use the default port but if it is busy, we offer the user to
|
|
// run on a different port. `detect()` Promise resolves to the next free port.
|
|
detect(DEFAULT_PORT).then(port => {
|
|
if (port === DEFAULT_PORT) {
|
|
run(port);
|
|
return;
|
|
}
|
|
|
|
clearConsole();
|
|
var question =
|
|
chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') +
|
|
'\n\nWould you like to run the app on another port instead?';
|
|
|
|
prompt(question, true).then(shouldChangePort => {
|
|
if (shouldChangePort) {
|
|
run(port);
|
|
}
|
|
});
|
|
});
|