Browse Source

optimize js build (#1145)

tags/6.1-RC1
Stas Vilchik 7 years ago
parent
commit
2d5742c852

+ 19
- 8
server/sonar-web/.babelrc View File

{ {
"presets": ["es2015", "stage-0", "react"],
"presets": ["es2015", "es2016", "react"],
"ignore": [ "ignore": [
"**/libs/**" "**/libs/**"
], ],
"plugins": [
"transform-class-properties",
"transform-object-rest-spread"
],
"env": { "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"]
}]
}] }]
}]]
]
} }
} }
} }

+ 2
- 0
server/sonar-web/.eslintignore View File

src/main/js/libs src/main/js/libs
tests tests
scripts
config

+ 1
- 1
server/sonar-web/.gitignore View File

node_modules/ node_modules/


# npm logs # npm logs
npm-debug.log.*
npm-debug.log
npm.tar.gz npm.tar.gz


# build # build

+ 28
- 0
server/sonar-web/config/autoprefixer.js View File

/*
* 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'
]
};

+ 27
- 0
server/sonar-web/config/paths.js View File

/*
* 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')
};

server/sonar-web/webpack.config.js → server/sonar-web/config/webpack/webpack.config.base.js View File

/* eslint no-var: 0 */ /* eslint no-var: 0 */
/* eslint object-shorthand: 0 */
/* jscs:disable requireEnhancedObjectLiterals */
var path = require('path'); var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
var autoprefixer = require('autoprefixer'); 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 = { module.exports = {
entry: { entry: {
'widgets': './src/main/js/widgets/widgets.js' 'widgets': './src/main/js/widgets/widgets.js'
}, },
output: { output: {
path: output,
path: paths.jsBuild,
filename: '[name].js' filename: '[name].js'
}, },
plugins: [ plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js') new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js')
], ],
resolve: { resolve: {
root: path.join(__dirname, 'src/main/js')
root: path.join(__dirname, '../../src/main/js')
}, },
module: { module: {
loaders: [ loaders: [
{ {
test: /\.js$/, test: /\.js$/,
loader: 'babel', loader: 'babel',
exclude: /(node_modules|libs)/
exclude: /(node_modules|libs)/,
}, },
{ {
test: /(blueimp-md5|numeral)/, test: /(blueimp-md5|numeral)/,
}, },
{ {
test: /\.hbs$/, test: /\.hbs$/,
loader: 'handlebars-loader',
loader: 'handlebars',
query: { query: {
helperDirs: path.join(__dirname, 'src/main/js/helpers/handlebars')
helperDirs: path.join(__dirname, '../../src/main/js/helpers/handlebars')
} }
}, },
{ {

server/sonar-web/webpack.config.dev.js → server/sonar-web/config/webpack/webpack.config.dev.js View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
/* eslint no-var: 0 */
var webpack = require('webpack'); var webpack = require('webpack');

var config = require('./webpack.config');
var config = require('./webpack.config.base');


config.devtool = 'cheap-module-eval-source-map'; config.devtool = 'cheap-module-eval-source-map';
config.output.publicPath = '/js/bundles/'; config.output.publicPath = '/js/bundles/';
config.entry.vendor.unshift('webpack-hot-middleware/client'); config.entry.vendor.unshift('webpack-hot-middleware/client');
config.plugins = [].concat(config.plugins, [ config.plugins = [].concat(config.plugins, [
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }),
new webpack.HotModuleReplacementPlugin() new webpack.HotModuleReplacementPlugin()
]); ]);



server/sonar-web/devServer.js → server/sonar-web/config/webpack/webpack.config.fast.js View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 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 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;

+ 34
- 0
server/sonar-web/config/webpack/webpack.config.prod.js View File

/*
* 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;

+ 0
- 34
server/sonar-web/gulp/styles.js View File

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')));
};

+ 26
- 63
server/sonar-web/gulpfile.js View File

var path = require('path');

/* eslint no-var: 0 */
var del = require('del'); var del = require('del');
var gulp = require('gulp'); 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) { 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 () { gulp.task('styles:prod', function () {
return styles(output, true);
return styles(paths.cssBuild, true);
}); });


gulp.task('styles:dev', function () { 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']);

+ 18
- 10
server/sonar-web/package.json View File

"devDependencies": { "devDependencies": {
"autoprefixer": "6.2.2", "autoprefixer": "6.2.2",
"babel-cli": "6.3.17", "babel-cli": "6.3.17",
"babel-core": "6.3.17",
"babel-core": "6.13.2",
"babel-eslint": "^6.0.4", "babel-eslint": "^6.0.4",
"babel-loader": "6.2.0",
"babel-loader": "6.2.4",
"babel-plugin-react-transform": "2.0.2", "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-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-preset-stage-0": "6.3.13",
"babel-register": "6.3.13", "babel-register": "6.3.13",
"backbone": "1.2.3", "backbone": "1.2.3",
"backbone.marionette": "2.4.3", "backbone.marionette": "2.4.3",
"blueimp-md5": "1.1.1", "blueimp-md5": "1.1.1",
"chai": "3.3.0", "chai": "3.3.0",
"chalk": "1.1.3",
"classnames": "2.2.0", "classnames": "2.2.0",
"clipboard": "1.5.5", "clipboard": "1.5.5",
"cross-env": "^2.0.0",
"css-loader": "0.23.1", "css-loader": "0.23.1",
"d3": "3.5.6", "d3": "3.5.6",
"del": "2.0.2", "del": "2.0.2",
"mockery": "1.7.0", "mockery": "1.7.0",
"moment": "2.10.6", "moment": "2.10.6",
"numeral": "1.5.3", "numeral": "1.5.3",
"nyc": "^8.1.0",
"postcss-loader": "0.8.0", "postcss-loader": "0.8.0",
"react": "15.0.1", "react": "15.0.1",
"react-addons-perf": "15.0.1", "react-addons-perf": "15.0.1",
"redux-logger": "2.2.1", "redux-logger": "2.2.1",
"redux-simple-router": "1.0.1", "redux-simple-router": "1.0.1",
"redux-thunk": "1.0.2", "redux-thunk": "1.0.2",
"rimraf": "2.5.4",
"script-loader": "0.6.1", "script-loader": "0.6.1",
"sinon": "1.15.4", "sinon": "1.15.4",
"sinon-chai": "2.8.0", "sinon-chai": "2.8.0",
"yargs": "3.27.0" "yargs": "3.27.0"
}, },
"scripts": { "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": { "engines": {
"node": ">=4" "node": ">=4"

+ 62
- 0
server/sonar-web/scripts/build.js View File

/*
* 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();
});

+ 158
- 0
server/sonar-web/scripts/start.js View File

/*
* 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);


Loading…
Cancel
Save