Skip to content

Commit

Permalink
[optimizer] More aggressive chunking of common/vendor code (elastic#1…
Browse files Browse the repository at this point in the history
…5907) (elastic#16051)

Previously, we were not aggressive in combining common code which resulted in duplicates included in the bundles.

As an example `node_modules/elasticsearch-browser/elasticsearch.angular.js` is present in the following chunks:

* kibana.bundle.js
* dashboardViewer.bundle.js
* apm.bundle.js
* monitoring.bundle.js
* ml.bundle.js
* timelion.bundle.js
* graph.bundle.js

Vendored code (anything inside node_modules) is placed in vendors.bundle.js while everything else with more than two references is placed in commons.bundle.js.

This has a couple positive side-effects (numbers are with x-pack & canvas):

* Decreased build time. Seeing builds go from 475.76 seconds to 274.72.
* Decreased memory overhead. Uses roughly 1/3 the memory overhead.
* Decreased bundle size. A 68% reduction in overall bundle size. Going from 66.16 MB to 21.13 MB.

Signed-off-by: Tyler Smalley <[email protected]>
  • Loading branch information
tylersmalley authored Jan 15, 2018
1 parent dd3a340 commit 1a79309
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 9 deletions.
24 changes: 15 additions & 9 deletions src/optimize/base_optimizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import { writeFile } from 'fs';
import Boom from 'boom';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import webpack from 'webpack';
import CommonsChunkPlugin from 'webpack/lib/optimize/CommonsChunkPlugin';
import DefinePlugin from 'webpack/lib/DefinePlugin';
import UglifyJsPlugin from 'webpack/lib/optimize/UglifyJsPlugin';
import NoEmitOnErrorsPlugin from 'webpack/lib/NoEmitOnErrorsPlugin';
import Stats from 'webpack/lib/Stats';
import webpackMerge from 'webpack-merge';

Expand Down Expand Up @@ -98,6 +94,8 @@ export default class BaseOptimizer {
});
}

const nodeModulesPath = fromRoot('node_modules');

/**
* Adds a cache loader if we're running in dev mode. The reason we're not adding
* the cache-loader when running in production mode is that it creates cache
Expand Down Expand Up @@ -141,12 +139,20 @@ export default class BaseOptimizer {
allChunks: true
}),

new CommonsChunkPlugin({
new webpack.optimize.CommonsChunkPlugin({
name: 'commons',
filename: 'commons.bundle.js'
filename: 'commons.bundle.js',
minChunks: 2,
}),

new webpack.optimize.CommonsChunkPlugin({
name: 'vendors',
filename: 'vendors.bundle.js',
// only combine node_modules from Kibana
minChunks: module => module.context && module.context.indexOf(nodeModulesPath) !== -1
}),

new NoEmitOnErrorsPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
],

module: {
Expand Down Expand Up @@ -233,12 +239,12 @@ export default class BaseOptimizer {

return webpackMerge(commonConfig, {
plugins: [
new DefinePlugin({
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
}),
new UglifyJsPlugin({
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
Expand Down
4 changes: 4 additions & 0 deletions src/optimize/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export default async (kbnServer, server, config) => {

await uiBundles.writeEntryFiles();

// Not all entry files produce a css asset. Ensuring they exist prevents
// an error from occuring when the file is missing.
await uiBundles.ensureStyleFiles();

// in prod, only bundle when someing is missing or invalid
const reuseCache = config.get('optimize.useBundleCache')
? await uiBundles.areAllBundleCachesValid()
Expand Down
2 changes: 2 additions & 0 deletions src/optimize/watch/watch_optimizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export default class WatchOptimizer extends BaseOptimizer {
this.status$.subscribe(this.onStatusChangeHandler);

await this.uiBundles.writeEntryFiles();
await this.uiBundles.ensureStyleFiles();

await this.initCompiler();

this.compiler.plugin('watch-run', this.compilerRunStartHandler);
Expand Down
18 changes: 18 additions & 0 deletions src/ui/ui_bundles/ui_bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export class UiBundle {
return this._controller.resolvePath(`${this.getId()}.entry.js`);
}

getStylePath() {
return this._controller.resolvePath(`${this.getId()}.style.css`);
}

getOutputPath() {
return this._controller.resolvePath(`${this.getId()}.bundle.js`);
}
Expand Down Expand Up @@ -63,6 +67,20 @@ export class UiBundle {
));
}

async hasStyleFile() {
return await fcb(cb => {
return stat(this.getStylePath(), error => {
cb(null, !(error && error.code === 'ENOENT'));
});
});
}

async touchStyleFile() {
return await fcb(cb => (
writeFile(this.getStylePath(), '', 'utf8', cb)
));
}

async clearBundleFile() {
try {
await fcb(cb => unlink(this.getOutputPath(), cb));
Expand Down
10 changes: 10 additions & 0 deletions src/ui/ui_bundles/ui_bundles_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ export class UiBundlesController {
}
}

async ensureStyleFiles() {
await this.ensureDir();

for (const bundle of this._bundles) {
if (!await bundle.hasStyleFile()) {
await bundle.touchStyleFile();
}
}
}

hashBundleEntries() {
const hash = createHash('sha1');
for (const bundle of this._bundles) {
Expand Down
3 changes: 3 additions & 0 deletions src/ui/ui_render/views/ui_app.jade
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ block content
return anchor.href;
}
var files = [
bundleFile('vendors.style.css'),
bundleFile('commons.style.css'),
bundleFile('#{app.getId()}.style.css'),

bundleFile('vendors.bundle.js'),
bundleFile('commons.bundle.js'),
bundleFile('#{app.getId()}.bundle.js')
];
Expand Down
6 changes: 6 additions & 0 deletions tasks/config/karma.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ module.exports = function (grunt) {

// list of files / patterns to load in the browser
files: [
'http://localhost:5610/bundles/vendors.bundle.js',
'http://localhost:5610/bundles/commons.bundle.js',
'http://localhost:5610/bundles/tests.bundle.js',

'http://localhost:5610/bundles/vendors.style.css',
'http://localhost:5610/bundles/commons.style.css',
'http://localhost:5610/bundles/tests.style.css'
],
Expand Down Expand Up @@ -126,8 +129,11 @@ module.exports = function (grunt) {
singleRun: true,
options: {
files: [
'http://localhost:5610/bundles/vendors.bundle.js',
'http://localhost:5610/bundles/commons.bundle.js',
`http://localhost:5610/bundles/tests.bundle.js?shards=${TOTAL_CI_SHARDS}&shard_num=${n}`,

'http://localhost:5610/bundles/vendors.style.css',
'http://localhost:5610/bundles/commons.style.css',
'http://localhost:5610/bundles/tests.style.css'
]
Expand Down

0 comments on commit 1a79309

Please sign in to comment.