Frontend pipeline with webpack 2: setting up babel (1 of 2)

An introduction about frontend pipeline in 2017 and how to build one from scratch using webpack 2: setting up babel traspiler.

Prologue

In case you don't know it, a frontend pipeline is a set of instructions useful to automate some tasks you usually perform on your assets before going to production, or while developing to make the development itself easier and faster. Here's some examples, just to give the idea:

  • Compile .scss/.sass into .css
  • Minify your code
  • Copy all your splitted code into a single file (usually for javascripts file)
  • Compile your html template (pug/jade, nunjucks, handlebars ...) into standard html

In this article we are going to write a basic pipeline which can take care of executing the tasks listed above and to achieve this we are using webpack 2, a module bundler that is becoming one of the most popular frameworks to build this kind stuff.

The first thing to know about webpack is that it is not a proper task runner like Gulp.js, webpack is a module bundler, that means its job is to take all your splitted files and put them together with the logic and constraints you decided in your config file.

Bundle javacript file together

Let's start writing some code so all will make more sense, we begin with the creation of a basic project structure:

  • Create a new folder for the project;
  • Inside that folder create two more folders: src (for all our assets) and dist (for the output);
  • In the project root create a javascript file that is going to contain the webpack config, usually it is called webpack.config.js, but you can choose whatever you prefer;

The easiest part to achieve with webpack is bundling together javascript files, this will be our first goal. So let's create a index.js file and a javascript folder with an index.js in it, all inside src, in order to have this folders structure:

Now we can start configuring webpack, but first you need to install it, of course, with $ npm install webpack --save-dev, then open webpack.config.js with your favorite editor and export an object as described in the docs:


module.exports = {
  entry: './app.js',
  output: {
    filename: 'bundle.js'
  }
}
    

webpack.config.js

Then we change it to suit our project structure:

  • entry: lets you define the entry point of all your assets, in our case it is "./src/index.js";
  • output: as the name suggests, defines where your build goes once is completed, we are using two more options of output
    • filename: simply the name of the file containing the build, usually something like build.js or main.js, but again you can choose whatever you like;
    • path: lets you specify the path to that file, it must be an absolute path, in our case we use node's helper __direname to get the absolute path to our current location, then we merge it with the relative one to the folder we are using as target through node's path module;

// webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist')
  }
}
    

webpack.config.js

We are finally ready to try it out, in order to run webpack type $ ./node_modules/.bin/webpack in your favorite shell inside the project root, or if you have webpack installed globally, just $ webpack.

In your dist folder you'll find a bundle.js file containing some javascript that webpack uses to bundle the different files, and nothing more since our index.js is empty, so let's move over an add some content to it.

webpack allows us to use the import/export features contained in ES2015 module import without the needs of babel or another transpiler, webpack itself takes care of wrapping that code into something usable by all browsers. We can proceed writing a couple functions and importing them in our index.js.


// .src/javascript/utils.js
export function foo() { console.log('foo'); };

export function bar() { console.log('bar'); };
    

.src/javascript/utils.js


// .src/javascript/index.js
import { foo, bar } from './utils.js';

foo();
bar();
    

.src/javascript/index.js

Of course we also have to import all our javascript stuff in the assets' entry point, which is ./src/index.js


// .src/index.js
import scripts from './javascript/'; // shortcut for './javascript/index.js'
    

.src/index.js

Run $ ./node_modules/.bin/webpack again and this time you'll see the imported code copied into the bundle file. Now you can link this file in your HTML and see the logs produced by the two functions.

Tree Shaking in webpack 2

webpack 2 ships with a really cool feature: it can detect unused imports and, more in general, unused code to produce a more efficient bundle, let's put it in practise:


// .src/javascript/index.js
import { foo, bar } from './utils.js';

foo();
//bar(); we are importing bar, but actually not using it
    

.src/javascript/index.js

If you run webpack and open bundle.js you'll see that bar() is marked as /* unused harmony export bar */, this means that on a production build that code won't be injected, you can easily verify this by running webpack with a -p flag that stands for production.

Add Babel traspiler

Now that we have a pipeline that can understand the import/export logic, we can move on adding some useful modules to speed up our developing routine, a very common one is Babel traspiler that allows us to write ES6 syntax without browser compatibility issues.

webpack is capable of understanding a large variety of modules, written in likewise of languages through loaders: a loader basically instructs webpack how to treat a file of a language it natively doesn't understand ( != javascript ) and/or how to perform some actions on it.
In our case we need a loader to handle ES6 syntax, specifically Babel-loader, so we can transpile ES6 to "native" javascript before putting the result in the bundle.js file.

As written on the loader's docs, we need to install a couple of node_modules $ npm install --save-dev babel-loader babel-core babel-preset-env like that, then we need to edit our webpack.config.js file to configure in which files webpack has to apply the loader:


// webpack.config.js
const path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['env']
          }
        }
      }
    ]
  }
}
    

webpack.config.js

  • test: is a regex to use in order to identify the files affected by the loader, in our case catch all .js files;
  • exclude: another regex to use as blacklist to all the files or folders you don't want to be affected by the loader, in our case we don't want to apply babel transpiling to the node_module stuff of course;
  • use: which loader to use;
  • options: options of the loader, we specify babel to use 'env' presets of rules, refer the babel docs for more infos about this;

And basically we are done with it ! If you start writing some ES6 code in your .js files and then run webpack, as we did previously, you'll se that in your bundle.js file what you wrote has been traspiled to be fully understandable by all browsers.

Conclusions

By now you should be able to create basic pipeline with webpack and have a general idea of how a loader works.

Checkout the next part of this serie where we are going to handle our stylesheets, compiling .sass/.scss into regular .css and more!

You may also like

Node.js flat CMS Tutorial with Enduro.js

A tutorial on how to get started with with Enduro.js, a Node.js flat CMS.

Tags: Node.js CMS

Read more

Node.js flat CMS Tutorial with Enduro.js - Part Two

Second part of a tutorial on how to get started with with Enduro.js, a Node.js flat CMS.

Tags: Node.js CMS

Read more

Javascript promises guide: using async await in both frontend and backend

A guide about javascript promises, what they are and how to use them effectively with async await.

Tags: JavaScript ES6 Node.js

Read more
See all