From: Stas Vilchik Date: Mon, 28 Nov 2016 17:13:00 +0000 (+0100) Subject: SONAR-8448 generate index.html during the build X-Git-Tag: 6.3-RC1~855 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=673b03c6f68593f4e27675e709f2ce20831e0d98;p=sonarqube.git SONAR-8448 generate index.html during the build --- 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.: + // + // In development, this will be an empty string. + new InterpolateHtmlPlugin({ + WEB_CONTEXT: webContext + }), + + // Generates an `index.html` file with the + <% for (var chunk in htmlWebpackPlugin.files.chunks) { %> + + <% } %> + + \ 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',