start.js 5.38 KiB
/**
 * 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);
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');
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
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 chrome.applescript http://localhost:' + port + '/', {cwd: path.join(__dirname, 'utils'), stdio: 'ignore'}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
); 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); } }); });