aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/.babelrc27
-rw-r--r--server/sonar-web/.eslintignore2
-rw-r--r--server/sonar-web/.gitignore2
-rw-r--r--server/sonar-web/config/autoprefixer.js28
-rw-r--r--server/sonar-web/config/paths.js27
-rw-r--r--server/sonar-web/config/webpack/webpack.config.base.js (renamed from server/sonar-web/webpack.config.js)19
-rw-r--r--server/sonar-web/config/webpack/webpack.config.dev.js (renamed from server/sonar-web/webpack.config.dev.js)5
-rw-r--r--server/sonar-web/config/webpack/webpack.config.fast.js (renamed from server/sonar-web/devServer.js)39
-rw-r--r--server/sonar-web/config/webpack/webpack.config.prod.js34
-rw-r--r--server/sonar-web/gulp/styles.js34
-rw-r--r--server/sonar-web/gulpfile.js89
-rw-r--r--server/sonar-web/package.json28
-rw-r--r--server/sonar-web/scripts/build.js62
-rw-r--r--server/sonar-web/scripts/start.js158
14 files changed, 389 insertions, 165 deletions
diff --git a/server/sonar-web/.babelrc b/server/sonar-web/.babelrc
index 0ecf1664f9c..fb5f2613035 100644
--- a/server/sonar-web/.babelrc
+++ b/server/sonar-web/.babelrc
@@ -1,17 +1,28 @@
{
- "presets": ["es2015", "stage-0", "react"],
+ "presets": ["es2015", "es2016", "react"],
"ignore": [
"**/libs/**"
],
+ "plugins": [
+ "transform-class-properties",
+ "transform-object-rest-spread"
+ ],
"env": {
- "hot": {
- "plugins": [["react-transform", {
- "transforms": [{
- "transform": "react-transform-hmr",
- "imports": ["react"],
- "locals": ["module"]
+ "production": {
+ "plugins": [
+ "transform-react-constant-elements"
+ ]
+ },
+ "development": {
+ "plugins": [
+ ["react-transform", {
+ "transforms": [{
+ "transform": "react-transform-hmr",
+ "imports": ["react"],
+ "locals": ["module"]
+ }]
}]
- }]]
+ ]
}
}
}
diff --git a/server/sonar-web/.eslintignore b/server/sonar-web/.eslintignore
index 1e86f01a222..80fae5d334e 100644
--- a/server/sonar-web/.eslintignore
+++ b/server/sonar-web/.eslintignore
@@ -1,2 +1,4 @@
src/main/js/libs
tests
+scripts
+config
diff --git a/server/sonar-web/.gitignore b/server/sonar-web/.gitignore
index 10ecba26bcc..4248ae850ed 100644
--- a/server/sonar-web/.gitignore
+++ b/server/sonar-web/.gitignore
@@ -5,7 +5,7 @@ node/
node_modules/
# npm logs
-npm-debug.log.*
+npm-debug.log
npm.tar.gz
# build
diff --git a/server/sonar-web/config/autoprefixer.js b/server/sonar-web/config/autoprefixer.js
new file mode 100644
index 00000000000..cfa9b836c52
--- /dev/null
+++ b/server/sonar-web/config/autoprefixer.js
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+module.exports = {
+ browsers: [
+ 'last 3 Chrome versions',
+ 'last 3 Firefox versions',
+ 'Safari >= 8',
+ 'Edge >= 12',
+ 'IE 11'
+ ]
+};
diff --git a/server/sonar-web/config/paths.js b/server/sonar-web/config/paths.js
new file mode 100644
index 00000000000..1484c1b0607
--- /dev/null
+++ b/server/sonar-web/config/paths.js
@@ -0,0 +1,27 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+var path = require('path');
+
+var base = process.env.OUTPUT || path.join(__dirname, '../src/main/webapp');
+
+module.exports = {
+ jsBuild: path.join(base, 'js/bundles'),
+ cssBuild: path.join(base, 'css')
+};
diff --git a/server/sonar-web/webpack.config.js b/server/sonar-web/config/webpack/webpack.config.base.js
index cea911812e7..f718dbd7dc2 100644
--- a/server/sonar-web/webpack.config.js
+++ b/server/sonar-web/config/webpack/webpack.config.base.js
@@ -1,14 +1,9 @@
/* eslint no-var: 0 */
-/* eslint object-shorthand: 0 */
-/* jscs:disable requireEnhancedObjectLiterals */
var path = require('path');
var webpack = require('webpack');
var autoprefixer = require('autoprefixer');
-
-var autoprefixerOptions = require('./gulp/styles').autoprefixerOptions;
-
-var baseOutput = process.env.OUTPUT || path.join(__dirname, 'src/main/webapp');
-var output = path.join(baseOutput, 'js/bundles');
+var autoprefixerOptions = require('./../autoprefixer');
+var paths = require('./../paths');
module.exports = {
entry: {
@@ -59,21 +54,21 @@ module.exports = {
'widgets': './src/main/js/widgets/widgets.js'
},
output: {
- path: output,
+ path: paths.jsBuild,
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js')
],
resolve: {
- root: path.join(__dirname, 'src/main/js')
+ root: path.join(__dirname, '../../src/main/js')
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
- exclude: /(node_modules|libs)/
+ exclude: /(node_modules|libs)/,
},
{
test: /(blueimp-md5|numeral)/,
@@ -81,9 +76,9 @@ module.exports = {
},
{
test: /\.hbs$/,
- loader: 'handlebars-loader',
+ loader: 'handlebars',
query: {
- helperDirs: path.join(__dirname, 'src/main/js/helpers/handlebars')
+ helperDirs: path.join(__dirname, '../../src/main/js/helpers/handlebars')
}
},
{
diff --git a/server/sonar-web/webpack.config.dev.js b/server/sonar-web/config/webpack/webpack.config.dev.js
index f0fc980c360..117450951a3 100644
--- a/server/sonar-web/webpack.config.dev.js
+++ b/server/sonar-web/config/webpack/webpack.config.dev.js
@@ -17,15 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-/* eslint no-var: 0 */
var webpack = require('webpack');
-
-var config = require('./webpack.config');
+var config = require('./webpack.config.base');
config.devtool = 'cheap-module-eval-source-map';
config.output.publicPath = '/js/bundles/';
config.entry.vendor.unshift('webpack-hot-middleware/client');
config.plugins = [].concat(config.plugins, [
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }),
new webpack.HotModuleReplacementPlugin()
]);
diff --git a/server/sonar-web/devServer.js b/server/sonar-web/config/webpack/webpack.config.fast.js
index 837e83ef104..87cc9383b3a 100644
--- a/server/sonar-web/devServer.js
+++ b/server/sonar-web/config/webpack/webpack.config.fast.js
@@ -17,40 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
- /* eslint no-var: 0 */
- /* eslint no-console: 0 */
- /* eslint object-shorthand: 0 */
- /* jscs:disable requireEnhancedObjectLiterals */
-var url = require('url');
-var express = require('express');
-var proxy = require('express-http-proxy');
var webpack = require('webpack');
-var config = require('./webpack.config.dev');
+var config = require('./webpack.config.base');
-var app = express();
-var compiler = webpack(config);
+config.plugins = [].concat(config.plugins, [
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }),
+]);
-var PORT = process.env.PORT || 8080;
-var API_HOST = process.env.API_HOST || 'http://localhost:9000';
-
-app.use(require('webpack-dev-middleware')(compiler, {
- noInfo: true,
- publicPath: config.output.publicPath
-}));
-
-app.use(require('webpack-hot-middleware')(compiler));
-
-app.all('*', proxy(API_HOST, {
- forwardPath: function (req) {
- return url.parse(req.url).path;
- }
-}));
-
-app.listen(PORT, 'localhost', function (err) {
- if (err) {
- console.log(err);
- return;
- }
-
- console.log('Listening at http://localhost:' + PORT);
-});
+module.exports = config;
diff --git a/server/sonar-web/config/webpack/webpack.config.prod.js b/server/sonar-web/config/webpack/webpack.config.prod.js
new file mode 100644
index 00000000000..7c5c3917986
--- /dev/null
+++ b/server/sonar-web/config/webpack/webpack.config.prod.js
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+var webpack = require('webpack');
+var config = require('./webpack.config.base');
+
+config.plugins = [].concat(config.plugins, [
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
+ new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.optimize.DedupePlugin(),
+ new webpack.optimize.UglifyJsPlugin({
+ compress: { screw_ie8: true, warnings: false },
+ mangle: { screw_ie8: true },
+ output: { screw_ie8: true, comments: false }
+ })
+]);
+
+module.exports = config;
diff --git a/server/sonar-web/gulp/styles.js b/server/sonar-web/gulp/styles.js
deleted file mode 100644
index 14d98861074..00000000000
--- a/server/sonar-web/gulp/styles.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var path = require('path');
-
-var gulp = require('gulp');
-var gulpif = require('gulp-if');
-var less = require('gulp-less');
-var nano = require('gulp-cssnano');
-var autoprefixer = require('gulp-autoprefixer');
-
-
-var nanoOptions = {
- zindex: false,
- discardComments: { removeAll: true }
-};
-
-var autoprefixerOptions = {
- browsers: [
- 'last 3 Chrome versions',
- 'last 3 Firefox versions',
- 'Safari >= 8',
- 'Edge >= 12',
- 'IE 11'
- ]
-};
-
-
-module.exports.autoprefixerOptions = autoprefixerOptions;
-
-module.exports.styles = function (output, production) {
- return gulp.src(['src/main/less/sonar.less'])
- .pipe(less())
- .pipe(autoprefixer(autoprefixerOptions))
- .pipe(gulpif(production, nano(nanoOptions)))
- .pipe(gulp.dest(path.join(output, 'css')));
-};
diff --git a/server/sonar-web/gulpfile.js b/server/sonar-web/gulpfile.js
index 517f94c6fd6..4f8baa3c1b1 100644
--- a/server/sonar-web/gulpfile.js
+++ b/server/sonar-web/gulpfile.js
@@ -1,75 +1,38 @@
-var path = require('path');
-
+/* eslint no-var: 0 */
var del = require('del');
var gulp = require('gulp');
-var gutil = require('gulp-util');
-var webpack = require('webpack');
-
-var argv = require('yargs').argv;
-var output = argv.output || path.join(__dirname, 'src/main/webapp');
-
-var styles = require('./gulp/styles').styles;
-var webpackConfig = require('./webpack.config.js');
-webpackConfig.output.path = path.join(output, 'js/bundles');
-
-
-// Clean
+var gulpif = require('gulp-if');
+var less = require('gulp-less');
+var nano = require('gulp-cssnano');
+var autoprefixer = require('gulp-autoprefixer');
+var paths = require('./config/paths');
+var autoprefixerOptions = require('./config/autoprefixer');
+
+var nanoOptions = {
+ zindex: false,
+ discardComments: { removeAll: true }
+};
+
+function styles (output, production) {
+ return gulp.src(['src/main/less/sonar.less'])
+ .pipe(less())
+ .pipe(autoprefixer(autoprefixerOptions))
+ .pipe(gulpif(production, nano(nanoOptions)))
+ .pipe(gulp.dest(output));
+}
gulp.task('clean', function (done) {
- del([
- path.join(output, 'js'),
- path.join(output, 'css')
- ], done);
+ del(paths.cssBuild, done);
});
-
-// Styles
-
gulp.task('styles:prod', function () {
- return styles(output, true);
+ return styles(paths.cssBuild, true);
});
gulp.task('styles:dev', function () {
- return styles(output, false, true);
-});
-
-
-// Webpack
-
-gulp.task('webpack:prod', function (callback) {
- var webpackProdConfig = Object.create(webpackConfig);
- webpackProdConfig.plugins = webpackProdConfig.plugins.concat(
- new webpack.DefinePlugin({
- 'process.env': {
- 'NODE_ENV': JSON.stringify('production'),
- 'OUTPUT': output
- }
- }),
- new webpack.optimize.DedupePlugin(),
- new webpack.optimize.UglifyJsPlugin()
- );
-
- webpack(webpackProdConfig, function (err) {
- if (err) {
- throw new gutil.PluginError('webpack:prod', err);
- }
- callback();
- });
+ return styles(paths.cssBuild, false);
});
-gulp.task('webpack:dev', function (callback) {
- // run webpack
- webpack(webpackConfig, function (err) {
- if (err) {
- throw new gutil.PluginError('webpack:dev', err);
- }
- callback();
- });
-});
-
-
-// Tasks
-
-gulp.task('build', ['clean', 'styles:prod', 'webpack:prod']);
-gulp.task('build:dev', ['clean', 'styles:dev', 'webpack:dev']);
-gulp.task('default', ['build']);
+gulp.task('default', ['clean', 'styles:prod']);
+gulp.task('build', ['clean', 'styles:prod']);
+gulp.task('build-fast', ['clean', 'styles:dev']);
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index 3bc34ccd86f..7ad51930cd0 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -7,21 +7,27 @@
"devDependencies": {
"autoprefixer": "6.2.2",
"babel-cli": "6.3.17",
- "babel-core": "6.3.17",
+ "babel-core": "6.13.2",
"babel-eslint": "^6.0.4",
- "babel-loader": "6.2.0",
+ "babel-loader": "6.2.4",
"babel-plugin-react-transform": "2.0.2",
+ "babel-plugin-transform-class-properties": "6.11.5",
+ "babel-plugin-transform-object-rest-spread": "6.8.0",
+ "babel-plugin-transform-react-constant-elements": "6.9.1",
"babel-polyfill": "6.3.14",
- "babel-preset-es2015": "6.3.13",
- "babel-preset-react": "6.3.13",
+ "babel-preset-es2015": "6.13.2",
+ "babel-preset-es2016": "6.11.3",
+ "babel-preset-react": "6.11.1",
"babel-preset-stage-0": "6.3.13",
"babel-register": "6.3.13",
"backbone": "1.2.3",
"backbone.marionette": "2.4.3",
"blueimp-md5": "1.1.1",
"chai": "3.3.0",
+ "chalk": "1.1.3",
"classnames": "2.2.0",
"clipboard": "1.5.5",
+ "cross-env": "^2.0.0",
"css-loader": "0.23.1",
"d3": "3.5.6",
"del": "2.0.2",
@@ -53,6 +59,7 @@
"mockery": "1.7.0",
"moment": "2.10.6",
"numeral": "1.5.3",
+ "nyc": "^8.1.0",
"postcss-loader": "0.8.0",
"react": "15.0.1",
"react-addons-perf": "15.0.1",
@@ -69,6 +76,7 @@
"redux-logger": "2.2.1",
"redux-simple-router": "1.0.1",
"redux-thunk": "1.0.2",
+ "rimraf": "2.5.4",
"script-loader": "0.6.1",
"sinon": "1.15.4",
"sinon-chai": "2.8.0",
@@ -83,12 +91,12 @@
"yargs": "3.27.0"
},
"scripts": {
- "build-fast": "gulp build:dev",
- "build": "gulp build",
- "test": "mocha --opts tests/mocha.opts src/main/js/**/__tests__/**/*",
- "coverage": "nyc --exclude tests,**/__tests__/** mocha --opts tests/mocha.opts src/main/js/**/__tests__/**/* && nyc report --reporter lcov --report-dir target/coverage",
- "lint": "eslint src/main/js",
- "dev": "NODE_ENV=hot node devServer"
+ "start": "node ./scripts/start.js",
+ "build-fast": "node ./scripts/build.js --fast && gulp build-fast",
+ "build": "node ./scripts/build.js && gulp build",
+ "test": "cross-env NODE_ENV=test mocha --opts tests/mocha.opts src/main/js/**/__tests__/**/*",
+ "coverage": "cross-env NODE_ENV=test nyc mocha --opts tests/mocha.opts src/main/js/**/__tests__/**/* && nyc report --reporter lcov --report-dir target/coverage",
+ "lint": "eslint src/main/js"
},
"engines": {
"node": ">=4"
diff --git a/server/sonar-web/scripts/build.js b/server/sonar-web/scripts/build.js
new file mode 100644
index 00000000000..0226ff25163
--- /dev/null
+++ b/server/sonar-web/scripts/build.js
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+process.env.NODE_ENV = 'production';
+
+var chalk = require('chalk');
+var fs = require('fs');
+var path = require('path');
+var rimrafSync = require('rimraf').sync;
+var webpack = require('webpack');
+var paths = require('../config/paths');
+
+var isFastBuild = process.argv.some(arg => arg.indexOf('--fast') > -1);
+
+var config = isFastBuild ?
+ require('../config/webpack/webpack.config.fast') :
+ require('../config/webpack/webpack.config.prod');
+
+// Remove all content but keep the directory so that
+// if you're in it, you don't end up in Trash
+console.log(chalk.cyan.bold('Cleaning output directory...'));
+console.log(paths.jsBuild + '/*');
+console.log();
+rimrafSync(paths.jsBuild + '/*');
+
+if (isFastBuild) {
+ console.log(chalk.magenta.bold('Running fast build...'));
+} else {
+ console.log(chalk.cyan.bold('Creating optimized production build...'));
+}
+console.log();
+
+webpack(config).run(function (err, stats) {
+ if (err) {
+ console.log(chalk.red.bold('Failed to create a production build!'));
+ console.log(chalk.red(err.message || err));
+ process.exit(1);
+ }
+
+ console.log(chalk.green.bold('Compiled successfully!'));
+
+ var jsonStats = stats.toJson();
+ var seconds = jsonStats.time / 1000;
+ console.log('Duration: ' + seconds.toFixed(2) + 's');
+ console.log();
+});
diff --git a/server/sonar-web/scripts/start.js b/server/sonar-web/scripts/start.js
new file mode 100644
index 00000000000..ceb9d61928a
--- /dev/null
+++ b/server/sonar-web/scripts/start.js
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+process.env.NODE_ENV = 'development';
+
+var url = require('url');
+var express = require('express');
+var proxy = require('express-http-proxy');
+var webpack = require('webpack');
+var chalk = require('chalk');
+var config = require('../config/webpack/webpack.config.dev.js');
+
+var app = express();
+
+var DEFAULT_PORT = process.env.PORT || 8080;
+var API_HOST = process.env.API_HOST || 'http://localhost:9000';
+
+var compiler;
+
+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 setupCompiler () {
+ compiler = webpack(config);
+
+ compiler.plugin('invalid', function () {
+ console.log(chalk.cyan.bold('Compiling...'));
+ });
+
+ compiler.plugin('done', function (stats) {
+ var hasErrors = stats.hasErrors();
+ var hasWarnings = stats.hasWarnings();
+ if (!hasErrors && !hasWarnings) {
+ console.log(chalk.green.bold('Compiled successfully!'));
+ 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.bold('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 runDevServer (port) {
+ app.use(require('webpack-dev-middleware')(compiler, {
+ noInfo: true,
+ quiet: true,
+ publicPath: config.output.publicPath
+ }));
+
+ app.use(require('webpack-hot-middleware')(compiler, {
+ noInfo: true,
+ quiet: true
+ }));
+
+ app.all('*', proxy(API_HOST, {
+ forwardPath: function (req) {
+ return url.parse(req.url).path;
+ }
+ }));
+
+ app.listen(port, 'localhost', function (err) {
+ if (err) {
+ console.log(err);
+ return;
+ }
+
+ console.log(chalk.green.bold(
+ 'The app is running at http://localhost:' + port + '/'));
+ console.log(chalk.cyan.bold('Compiling...'));
+ console.log();
+ });
+}
+
+function run (port) {
+ setupCompiler();
+ runDevServer(port);
+}
+
+run(DEFAULT_PORT);
+