aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-11-28 18:13:00 +0100
committerStas Vilchik <vilchiks@gmail.com>2016-12-07 14:36:18 +0100
commit673b03c6f68593f4e27675e709f2ce20831e0d98 (patch)
tree30ee597d3a3e1a0bcf5eff9a7859d1084c32d435
parenta2d0b4ee581e86b474a47e16095e7500c404e37d (diff)
downloadsonarqube-673b03c6f68593f4e27675e709f2ce20831e0d98.tar.gz
sonarqube-673b03c6f68593f4e27675e709f2ce20831e0d98.zip
SONAR-8448 generate index.html during the build
-rw-r--r--server/sonar-web/.gitignore1
-rw-r--r--server/sonar-web/config/paths.js6
-rw-r--r--server/sonar-web/config/webpack/webpack.config.base.js15
-rw-r--r--server/sonar-web/config/webpack/webpack.config.dev.js35
-rw-r--r--server/sonar-web/config/webpack/webpack.config.fast.js22
-rw-r--r--server/sonar-web/config/webpack/webpack.config.prod.js33
-rw-r--r--server/sonar-web/package.json1
-rw-r--r--server/sonar-web/public/index.html18
-rw-r--r--server/sonar-web/scripts/build.js93
-rw-r--r--server/sonar-web/scripts/start.js17
10 files changed, 168 insertions, 73 deletions
diff --git a/server/sonar-web/.gitignore b/server/sonar-web/.gitignore
index e46e257c426..ffa1343f64d 100644
--- a/server/sonar-web/.gitignore
+++ b/server/sonar-web/.gitignore
@@ -12,6 +12,7 @@ npm.tar.gz
build/
src/main/webapp/js/
src/main/webapp/css/
+src/main/webapp/index.html
src/main/webapp/WEB-INF/lib/*.jar
# tests
diff --git a/server/sonar-web/config/paths.js b/server/sonar-web/config/paths.js
index 0ce56b422a5..3773a6a6439 100644
--- a/server/sonar-web/config/paths.js
+++ b/server/sonar-web/config/paths.js
@@ -45,10 +45,14 @@ var nodePaths = (process.env.NODE_PATH || '')
// config after eject: we're in ./config/
module.exports = {
- appBuild: resolveApp('src/main/webapp/js/bundles'),
+ appBuild: resolveApp('src/main/webapp'),
+ appPublic: resolveApp('public'),
+ appHtml: resolveApp('public/index.html'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src/main/js'),
+ jsBuild: resolveApp('src/main/webapp/js'),
cssBuild: resolveApp('src/main/webapp/css'),
+ htmlBuild: resolveApp('src/main/webapp/index.html'),
appNodeModules: resolveApp('node_modules'),
ownNodeModules: resolveApp('node_modules'),
nodePaths: nodePaths
diff --git a/server/sonar-web/config/webpack/webpack.config.base.js b/server/sonar-web/config/webpack/webpack.config.base.js
index 841c4a9710a..1d09d656426 100644
--- a/server/sonar-web/config/webpack/webpack.config.base.js
+++ b/server/sonar-web/config/webpack/webpack.config.base.js
@@ -1,10 +1,7 @@
-/* eslint no-var: 0 */
var path = require('path');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
-var url = require('url');
var paths = require('../paths');
var autoprefixerOptions = require('../autoprefixer');
@@ -25,19 +22,13 @@ module.exports = {
'sonar': './src/main/js/libs/sonar.js',
- 'app': './src/main/js/app/index.js',
-
- // not unique url
- 'source-viewer': './src/main/js/apps/source-viewer/app.js'
+ 'app': './src/main/js/app/index.js'
},
output: {
path: paths.appBuild,
- filename: '[name].js'
+ filename: 'js/[name].[chunkhash:8].js',
+ chunkFilename: 'js/[name].[chunkhash:8].chunk.js',
},
- plugins: [
- new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
- new ExtractTextPlugin('../../css/sonar.css', { allChunks: true })
- ],
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We read `NODE_PATH` environment variable in `paths.js` and pass paths here.
diff --git a/server/sonar-web/config/webpack/webpack.config.dev.js b/server/sonar-web/config/webpack/webpack.config.dev.js
index 6a2560262c4..fde4c94ca9f 100644
--- a/server/sonar-web/config/webpack/webpack.config.dev.js
+++ b/server/sonar-web/config/webpack/webpack.config.dev.js
@@ -18,16 +18,17 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
var webpack = require('webpack');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+var ExtractTextPlugin = require('extract-text-webpack-plugin');
+var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
var paths = require('../paths');
var config = require('./webpack.config.base');
var getClientEnvironment = require('../env');
-// Webpack uses `publicPath` to determine where the app is being served from.
-var publicPath = '/js/bundles/';
-
-// Get environment variables to inject into our app.
+var publicPath = '';
+var webContext = '';
var env = getClientEnvironment();
// This makes the bundle appear split into separate modules in the devtools.
@@ -53,22 +54,44 @@ config.output.pathinfo = true;
// This is the URL that app is served from.
config.output.publicPath = publicPath;
+config.output.filename = 'js/[name].js';
+config.output.chunkFilename = 'js/[name].chunk.js';
+
+config.plugins = [
+ new webpack.optimize.CommonsChunkPlugin('vendor', 'js/vendor.js'),
+
+ new ExtractTextPlugin('css/sonar.css', { allChunks: true }),
+
+ // Makes the web context available as %WEB_CONTEXT% in index.html, e.g.:
+ // <link rel="shortcut icon" href="%WEB_CONTEXT%/favicon.ico">
+ // In development, this will be an empty string.
+ new InterpolateHtmlPlugin({
+ WEB_CONTEXT: webContext
+ }),
+
+ // Generates an `index.html` file with the <script> injected.
+ new HtmlWebpackPlugin({
+ inject: false,
+ template: paths.appHtml,
+ }),
-config.plugins = [].concat(config.plugins, [
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
new webpack.DefinePlugin(env),
+
// This is necessary to emit hot updates (currently CSS only):
new webpack.HotModuleReplacementPlugin(),
+
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebookincubator/create-react-app/issues/240
new CaseSensitivePathsPlugin(),
+
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebookincubator/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
-]);
+];
module.exports = config;
diff --git a/server/sonar-web/config/webpack/webpack.config.fast.js b/server/sonar-web/config/webpack/webpack.config.fast.js
index 0481d4fc6cd..f0fe4003a3e 100644
--- a/server/sonar-web/config/webpack/webpack.config.fast.js
+++ b/server/sonar-web/config/webpack/webpack.config.fast.js
@@ -17,9 +17,31 @@
* 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 ExtractTextPlugin = require('extract-text-webpack-plugin');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
var config = require('./webpack.config.base');
+var getClientEnvironment = require('../env');
+var paths = require('../paths');
+
+// Get environment variables to inject into our app.
+var env = getClientEnvironment();
// disable eslint loader
config.module.preLoaders = [];
+// Don't attempt to continue if there are any errors.
+config.bail = true;
+
+config.plugins = [
+ new webpack.optimize.CommonsChunkPlugin('vendor', 'js/vendor.[chunkhash:8].js'),
+
+ new ExtractTextPlugin('css/sonar.[chunkhash:8].css', { allChunks: true }),
+
+ new HtmlWebpackPlugin({
+ inject: false,
+ template: paths.appHtml
+ })
+];
+
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
index 43c0a672418..0d351f8eaf1 100644
--- a/server/sonar-web/config/webpack/webpack.config.prod.js
+++ b/server/sonar-web/config/webpack/webpack.config.prod.js
@@ -18,8 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
var webpack = require('webpack');
+var ExtractTextPlugin = require('extract-text-webpack-plugin');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
var config = require('./webpack.config.base');
var getClientEnvironment = require('../env');
+var paths = require('../paths');
// Get environment variables to inject into our app.
var env = getClientEnvironment();
@@ -33,16 +36,40 @@ if (env['process.env.NODE_ENV'] !== '"production"') {
// Don't attempt to continue if there are any errors.
config.bail = true;
-config.plugins = [].concat(config.plugins, [
+config.plugins = [
+ new webpack.optimize.CommonsChunkPlugin('vendor', 'js/vendor.[chunkhash:8].js'),
+
+ new ExtractTextPlugin('css/sonar.[chunkhash:8].css', { allChunks: true }),
+
+ new HtmlWebpackPlugin({
+ inject: false,
+ template: paths.appHtml,
+ minify: {
+ removeComments: true,
+ collapseWhitespace: true,
+ removeRedundantAttributes: true,
+ useShortDoctype: true,
+ removeEmptyAttributes: true,
+ removeStyleLinkTypeAttributes: true,
+ keepClosingSlash: true,
+ minifyJS: true,
+ minifyCSS: true,
+ minifyURLs: true
+ }
+ }),
+
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env),
+
// This helps ensure the builds are consistent if source hasn't changed:
new webpack.optimize.OccurrenceOrderPlugin(),
+
// Try to dedupe duplicated modules, if any:
new webpack.optimize.DedupePlugin(),
+
// Minify the code.
new webpack.optimize.UglifyJsPlugin({
compress: {
@@ -56,7 +83,7 @@ config.plugins = [].concat(config.plugins, [
comments: false,
screw_ie8: true
}
- }),
-]);
+ })
+];
module.exports = config;
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index f0b6e5829c0..fc1431e98a7 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -48,6 +48,7 @@
"handlebars": "2.0.0",
"handlebars-loader": "1.1.4",
"history": "2.0.0",
+ "html-webpack-plugin": "2.24.1",
"imports-loader": "0.6.5",
"jest": "15.1.1",
"jquery": "2.2.0",
diff --git a/server/sonar-web/public/index.html b/server/sonar-web/public/index.html
new file mode 100644
index 00000000000..0864aeb17d2
--- /dev/null
+++ b/server/sonar-web/public/index.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <link href="%WEB_CONTEXT%/favicon.ico" rel="shortcut icon" type="image/x-icon">
+ <% for (var css in htmlWebpackPlugin.files.css) { %>
+ <link href="%WEB_CONTEXT%/<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
+ <% } %>
+ <title>SonarQube</title>
+ </head>
+ <body>
+ <div id="content"></div>
+ <script>window.baseUrl = '%WEB_CONTEXT%';</script>
+ <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
+ <script src="%WEB_CONTEXT%/<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
+ <% } %>
+ </body>
+</html> \ No newline at end of file
diff --git a/server/sonar-web/scripts/build.js b/server/sonar-web/scripts/build.js
index c73a5a9559f..8cc9bbef3c6 100644
--- a/server/sonar-web/scripts/build.js
+++ b/server/sonar-web/scripts/build.js
@@ -20,7 +20,7 @@
process.env.NODE_ENV = 'production';
var chalk = require('chalk');
-var fs = require('fs');
+var fs = require('fs-extra');
var path = require('path');
var rimrafSync = require('rimraf').sync;
var webpack = require('webpack');
@@ -33,54 +33,73 @@ var config = isFastBuild ?
require('../config/webpack/webpack.config.fast') :
require('../config/webpack/webpack.config.prod');
+function clean () {
// 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 directories...'));
+ console.log(chalk.cyan.bold('Cleaning output directories and files...'));
-console.log(paths.appBuild + '/*');
-rimrafSync(paths.appBuild + '/*');
+ console.log(paths.jsBuild + '/*');
+ rimrafSync(paths.jsBuild + '/*');
-console.log(paths.cssBuild + '/*');
-rimrafSync(paths.cssBuild + '/*');
+ console.log(paths.cssBuild + '/*');
+ rimrafSync(paths.cssBuild + '/*');
-console.log();
+ console.log(paths.htmlBuild);
+ rimrafSync(paths.htmlBuild);
-if (isFastBuild) {
- console.log(chalk.magenta.bold('Running fast build...'));
-} else {
- console.log(chalk.cyan.bold('Creating optimized production build...'));
+ console.log();
}
-console.log();
-webpack(config, (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);
+function build () {
+ if (isFastBuild) {
+ console.log(chalk.magenta.bold('Running fast build...'));
+ } else {
+ console.log(chalk.cyan.bold('Creating optimized production build...'));
}
+ console.log();
- if (stats.compilation.errors && stats.compilation.errors.length) {
- console.log(chalk.red.bold('Failed to create a production build!'));
- stats.compilation.errors.forEach(err => console.log(chalk.red(err.message || err)));
- process.exit(1);
- }
+ webpack(config, (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);
+ }
+
+ if (stats.compilation.errors && stats.compilation.errors.length) {
+ console.log(chalk.red.bold('Failed to create a production build!'));
+ stats.compilation.errors.forEach(err => console.log(chalk.red(err.message || err)));
+ process.exit(1);
+ }
+
+ var jsonStats = stats.toJson();
+
+ console.log('Assets:');
+ var assets = jsonStats.assets.slice();
+ assets.sort((a, b) => b.size - a.size);
+ assets.forEach(asset => {
+ var sizeLabel = formatSize(asset.size);
+ var leftPadding = ' '.repeat(Math.max(0, 8 - sizeLabel.length));
+ sizeLabel = leftPadding + sizeLabel;
+ console.log('', chalk.yellow(sizeLabel), asset.name);
+ });
+ console.log();
- var jsonStats = stats.toJson();
+ var seconds = jsonStats.time / 1000;
+ console.log('Duration: ' + seconds.toFixed(2) + 's');
+ console.log();
- console.log('Assets:');
- var assets = jsonStats.assets.slice();
- assets.sort((a, b) => b.size - a.size);
- assets.forEach(asset => {
- var sizeLabel = formatSize(asset.size);
- var leftPadding = ' '.repeat(8 - sizeLabel.length);
- sizeLabel = leftPadding + sizeLabel;
- console.log('', chalk.yellow(sizeLabel), asset.name);
+ console.log(chalk.green.bold('Compiled successfully!'));
});
- console.log();
+}
+
+function copyPublicFolder () {
+ fs.copySync(paths.appPublic, paths.appBuild, {
+ dereference: true,
+ filter: file => file !== paths.appHtml
+ });
+}
- var seconds = jsonStats.time / 1000;
- console.log('Duration: ' + seconds.toFixed(2) + 's');
- console.log();
- console.log(chalk.green.bold('Compiled successfully!'));
-});
+clean();
+build();
+copyPublicFolder();
diff --git a/server/sonar-web/scripts/start.js b/server/sonar-web/scripts/start.js
index 67b239a4739..c0ee1f90fe1 100644
--- a/server/sonar-web/scripts/start.js
+++ b/server/sonar-web/scripts/start.js
@@ -45,19 +45,6 @@ var handleCompile;
var PROXY_URL = 'http://localhost:9000';
-// You can safely remove this after ejecting.
-// We only use this block for testing of Create React App itself:
-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);
- }
- };
-}
-
function setupCompiler (host, port, protocol) {
// "Compiler" is a low-level interface to Webpack.
// It lets us listen to some events and provide our own custom messages.
@@ -181,7 +168,7 @@ function addMiddleware (devServer) {
// - /*.hot-update.json (WebpackDevServer uses this too for hot reloading)
// - /sockjs-node/* (WebpackDevServer uses this for hot reloading)
// Tip: use https://jex.im/regulex/ to visualize the regex
- var mayProxy = /^(?!\/(.*\.hot-update\.json$|sockjs-node\/)).*$/;
+ var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/;
devServer.use(mayProxy,
// Pass the scope regex both to Express and to the middleware for proxying
// of both HTTP and WebSockets to work without false positives.
@@ -201,6 +188,8 @@ function addMiddleware (devServer) {
function runDevServer (host, port, protocol) {
var devServer = new WebpackDevServer(compiler, {
+ // Enable gzip compression of generated files.
+ compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',