Software Build Tools Webpack | 2017-05-21

Webpack is the new “thing”, most of us have heard about it, but what about it makes it great?

  • It avoid a lot of the problems that predecessors have
  • Getting started is really simple
  • It has a plugin system, so it’s quite powerful if you need it.

Webpack handles the build of JS assets including “.appclass.js” files including

  • Parsing .vue files
  • Compiling SCSS and ES6
  • Polyfilling for IE
  • Formatting standards and linting

Webpack.config.js - Basic Sample

You can put commands you’d normally try out in the cmd line, in a config file.

var debug = process.env.NODE_ENV !== "production"; var webpack = require('webpack'); module.exports = { context: dirname, devtool: debug ? "inline-sourcemap" : false, entry: "./js/scripts.js", output: { path: dirname + "/js", filename: "scripts.min.js" }, plugins: debug ? [] : [ new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }), ], }; Webpack config - F2, React, Babel, and ES2015 Example module.exports = { entry: './apps/apps.js', output: { path: './build', filename: 'appBundle.js' }, module : { loaders : [{ test: /.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'react'] } }, { test: /.css$/, loader: "style-loader!css-loader" }, { test: /.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' } ] } };

The loaders section is the most important, essentially you have a set objects (.js, .css, and images) executed from the bottom up, and how webpack loads these files is unique to each one. Babel is used (not necessary), node_modules is excluded, and the .js loader includes a query preset option, it’s a way to pass parameters to a loader. You can't define a query parameter and multiple loaders.

Webpack config example - Plugins, output and entry

var path = require("path"); var CommonsChunkPlugin = require("./node_modules/webpack/lib/optimize/CommonsChunkPlugin"); module.exports = { entry: { app1: "./src/apps/app1/app.js", app2: "./src/apps/app2/app.js", container: "./src/container/f2Container.js" }, output: { path: path.join(dirname, "dist"), filename: "[name].bundle.js", chunkFilename: "[id].chunk.js" }, plugins: [ new CommonsChunkPlugin({ filename: "commons.js", name: "commons" }) ], module : { loaders : [{ test: /.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'react'] } }, { test: /.css$/, loader: "style-loader!css-loader" }, { test: /.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' } ] } };

This is similar to the last example, the major difference is the plugins section. Plugins are helpful because they help bridge simple webpack setup’s to more advanced versions. This example CommonsChunkPlugin, creates a common file (chunk) that can be shared across multiple points.

Webpack example - Vue, ESLint, Css & Html Loaders

var path = require('path'); var config = require('./config'); var utils = require('./utils'); var glob = require('glob'); var projectRoot = path.resolve(dirname, '../'); var jsonImporter = require('node-sass-json-importer'); var env = process.env.NODE_ENV; // check env & config/index.js to decide weither to enable CSS Sourcemaps for the // various preprocessor loaders added to vue-loader at the end of this file var cssSourceMapDev = (env === 'development' &&; var cssSourceMapProd = (env === 'production' &&; var useCssSourceMap = cssSourceMapDev || cssSourceMapProd; var patharr = glob.sync('./src/**/.entry.js'); //generating key value pairs from our array of paths //the app name is the key, and the relative path is the value var entryobj = patharr.reduce(function(result, item) { var newkey = item.substring(item.lastIndexOf('/') + 1, item.lastIndexOf('.entry.js')); if (newkey in result) { throw new Error(Existing filename detected: ${newkey}. Please make sure you dont have duplicate entry files); } result[newkey] = item; return result; }, {}); //loader for standalone style files var styleLoader = utils.styleLoaders({ sourceMap: useCssSourceMap, extract: false }) var loaders = [ { test: /.html$/, loader: 'html-loader' }, { test: /.vue$/, loader: 'vue' }, { test: /.js$/, loader: 'babel', include: projectRoot, exclude: /node_modules/ }, { test: /.json$/, loader: 'json' }, { test: /.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' } ]; var finalLoaders = loaders.concat(styleLoader); module.exports = { entry: entryobj, resolve: { //empty string is needed to resolve modules with extensions extensions: ['', '.js', '.vue', '.scss', '.json'], //where to fallback is module is not found fallback: [path.join(dirname, '../node_modules')], alias: { //need to alias vue to the compiled build or templates will not work vue: 'vue/dist/vue.js', 'shared': path.resolve(dirname, '../src/shared') } }, //where to fallback if we cant cant find a loader resolveLoader: { fallback: [path.join(__dirname, '../node_modules')] }, module: { preLoaders: [ { test: /.vue$/, loader: 'eslint', include: projectRoot, exclude: /node_modules/ }, { test: /.js$/, loader: 'eslint', include: projectRoot, exclude: /node_modules/ } ], loaders: finalLoaders }, eslint: { formatter: require('eslint-friendly-formatter'), failOnError: true }, vue: { loaders: utils.cssLoaders({ sourceMap: useCssSourceMap, extract: false }), //Adds browser prefixes like -ms- to necessary css styles postcss: [ require('autoprefixer')({ browsers: ['last 2 versions'] }) ] }, sassLoader: { // Apply the JSON importer via sass-loader's options. importer: jsonImporter } };

Whoa, that’s a lot!! It went from barely anything to EVERYTHING. What now?


  • html (using html-loader)
  • js (using babel)
  • json
  • vue
  • images (using url-loader)
  • css (using either dev or prod’s source map)


  • Will be executed first (preloaders, loaders, loaders in the request (require statements), and postLoaders.
  • Good example is for preLoaders are eslint reules and image compression.