Webpack2 : an example, a real one ?

« Has this ever happened to you ? »
-> You work in a non open minded company, or a company willing to make some progress, but behind in tech.
-> The front part of your stack is in (Java/PHP/Python/You name it) + (naive javascript or some former trendy framework like jquery / backbone / handlebars), and your build tool is as shifted from reality as maven or ant can be.
-> You think you deserve more than to maintain such a legacy stack and to write IE9 compliant code

My solution was to think with Webpack2.

Rather than explaining you what it is, let me define you what it is not :

– Webpack is not (really) a build tool. It would rather be « a module emulation builder ». In other words, it uses all of your resources to bundle them into very few « big files ».
Which means that you will no longer see this :
[Long resources list loading]
…but actually two resources : a webpage and a big script. Which will save the patience of some mobile users, and avoid repetitive server requests to load the same code over and over again.

– Webpack is not a code minifier. It groups css, medias (sound/picture), javascript resources at once. Don’t call that a minifier.

– Webpack is not a compiler. It uses a grammar to filter which resources must be handled, and some rules to declare how they should be transpiled/compressed/grouped/moved/compiled. Actually, it uses some loaders that are able to do the compiling job. Like babel, to be able to code using the latest javascript version (ES2017) while targeting IE9… Like handlebars, even if it is old, if you like it.

– Webpack is not a file manipulation tool. But it can add/change/delete some text inside files, it can copy/cut/paste/zip/unzip some resources. Some plugins are dedicated to such operations, like the CopyWebpackPlugin, or the ExtractTextPlugin.

Enough talk, let’s take an example stack : a « mavened » java webapp with the following js stack :
handlebars, backbone, underscore, jquery, and an invasive homegrown framework to avoid some non homogeneous display.
1°) Start by removing all of the DIY maven plugins. Write this instead in the plugins section:

                        <id>install node and yarn</id>
                        <id>yarn install</id>

2°) Start a small package.json, it will be the bootstrap of your webpack pipeline :

  "name": "my-webapp",
  "description": "EcmaScript pipeline to build the assets of my-webapp",
  "version": "1.0.0",
  "private": true,
  "devDependencies": {
    "webpack": "latest",
    "copy-webpack-plugin": "latest",
    "extract-text-webpack-plugin": "latest",
    "before-build-webpack": "latest",
    "noop-webpack-plugin": "latest",
    "modernizr-webpack-plugin": "latest",
    "style-loader": "latest",
    "css-loader": "latest",
    "less-loader": "latest",
    "img-loader": "latest",
    "url-loader": "latest",
    "imports-loader": "latest",
    "exports-loader": "latest",
    "babel-cli": "latest",
    "babel-core": "latest",
    "babel-loader": "latest",
    "babel-preset-env": "latest",
    "babel-preset-es2015": "latest",
    "babel-polyfill": "latest",
    "handlebars": "latest",
    "handlebars-loader": "latest",
    "file-loader": "latest",
    "modernizr": "latest",
    "modernizr-loader": "latest",
    "jquery": "latest",
    "unzip2": "latest",
    "underscore": "latest",
    "phoneparser": "latest",
    "intl-tel-input": "latest",
    "backbone": "latest",
    "moment": "latest",
    "i18n-js": "latest",
    "mkdirp": "latest",
    "ncp": "latest",
    "less": "latest",
    "glob": "latest",
    "properties-reader": "latest"
  "engines": {
    "node": ">=7.9.0"

3°) Write a small webpack pipeline (wepack.config.js) and test it immediately :

const webpack = require('webpack')
const path = require('path')
const fs = require('fs')
const unzip = require('unzip2')
const glob = require('glob')
const WebpackBeforeBuildPlugin = require('before-build-webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ModernizrWebpackPlugin = require('modernizr-webpack-plugin'

const javascriptFiles = glob.sync(`js/**/*.js`)
const target = path.join(__dirname, 'target/')

module.exports = {
    name: 'webapp-bundle',
    target: 'web',
    entry:  [{`js/bundles/bundle.js`: javascriptFiles,}], output: { path: target, filename: '[name].js' }, module: { rules: [ { test: /\.js$/, use: [{ loader: 'babel-loader', options: { presets: [['es2015']] } }] }, ] } }

That works, but there are so much work to do for a real webapp if you are not working on a hello world page or a calculator, and over all if you must cope with an homegrown framework from your company

4°) Add some plugins to your pipeline :
– prepare your assets before the build (unzip, copy, …) :WebpackBeforeBuildPlugin
– provide some variables that nobody wants to declare explicitely : jquery ($), underscore or lodash (_) :webpack.ProvidePlugin
– extract your assets in non js files, for example for css files : ExtractTextPlugin
– manipulate the results after the compilation step : CopyWebpackPlugin
my own plugins list looks like this one :

plugins: [beforeBuild, listAllHandlebarsFiles, provideGlobalVariables, new ModernizrWebpackPlugin(), /* compile, */ extractCss, copyResults, minify]

5°) Add some rules to help webpack understands what is supposed to happen for each case :

                test: /\.hbs$/,
                use: [{
                    loader: 'handlebars-loader',
                    options: {
                        knownHelpers: ['i18nMsg', 'block', 'include', 'includeJsBundle', 'eq', 'json', 'i18nJs',
                        partialDirs: [
                            path.join(__dirname, `${webpackAssetsFolder}views`)
                        rootRelatve: `${webpackAssetsFolder}views`
                test: /\.js$/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: [['es2015']]
                test: /\.less$/,
                use: extractCss.extract({
                    use: ['css-loader', 'less-loader']
                test: /\.css$/,
                use: extractCss.extract({
                    fallback: 'style-loader',
                    use: 'css-loader'

5°) Adapt your homegrown framework to your pipeline. Unzip the resources of the framework and mix them with yours.

You can have a look at how I did at my work : https://gist.github.com/libetl/79023dec57e4f4a02b48ee05b8d34d68
The pipeline (I mean the webpack.config.js file) can be composed of all these steps (mainly) :

import some modules
definition of resources and paths
definitions of complex plugins
output folder and output bundles
source maps (yes or no ?)
webpack rules (which file for which operation)
plugins list

Please do this at home, or at school. You will make your app more reliable and efficient. Good luck.