]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8448 generate index.html during the build
authorStas Vilchik <vilchiks@gmail.com>
Mon, 28 Nov 2016 17:13:00 +0000 (18:13 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 7 Dec 2016 13:36:18 +0000 (14:36 +0100)
server/sonar-web/.gitignore
server/sonar-web/config/paths.js
server/sonar-web/config/webpack/webpack.config.base.js
server/sonar-web/config/webpack/webpack.config.dev.js
server/sonar-web/config/webpack/webpack.config.fast.js
server/sonar-web/config/webpack/webpack.config.prod.js
server/sonar-web/package.json
server/sonar-web/public/index.html [new file with mode: 0644]
server/sonar-web/scripts/build.js
server/sonar-web/scripts/start.js

index e46e257c42629452d41277d7817156b9f16471f3..ffa1343f64d66651f39503360fcee4f0d0971c23 100644 (file)
@@ -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
index 0ce56b422a5f15e0c0f3aa1791dd3294ea6f9bf3..3773a6a64397be10a15cb7c461b2410a3438a0ff 100644 (file)
@@ -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
index 841c4a9710a774f4c50c953127fe289447a6db0d..1d09d65642648fb6be0cf3aaa1a91e83469a2622 100644 (file)
@@ -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.
index 6a2560262c47ebafde29c53aa799a3c26917254f..fde4c94ca9fb8d54bd73fd87426319ff82e35ebd 100644 (file)
  * 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;
index 0481d4fc6cde118fee1ea687f8be02579654cc0f..f0fe4003a3e01b7aa1c7e577f37a84b53c5d94c0 100644 (file)
  * 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;
index 43c0a6724181890aff392bcb80d8499cb3124823..0d351f8eaf1e2cef440d99d70f5d5545a886277a 100644 (file)
  * 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;
index f0b6e5829c08735cb0a9348a93fe5b95a02b0d43..fc1431e98a75dad8322dfc7b36410333cfa4aca3 100644 (file)
@@ -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 (file)
index 0000000..0864aeb
--- /dev/null
@@ -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
index c73a5a9559f2c8b67f2f833b62fe939b381748b8..8cc9bbef3c6e78c7400a3a5054ede3ad985d5622 100644 (file)
@@ -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();
index 67b239a47397a987576f8b74941c26f0d37fb90c..c0ee1f90fe1a3c1c442bedac132ee17282ff614d 100644 (file)
@@ -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',