Using a build tool

The first part of this series will cover build tools. Building an application means transforming your source files in some way into a production-ready bundle. We will touch upon the following topics in this article

To go straight to the practical application, you can skip ahead to the example section.

Using a build tool

Adding a build step to your application allows you to utilize other libraries with your app. One of the most popular usecases for build tools is transpiling source code. Other commonly used build tools aggregate and minimize assets, manage dependencies or automate tests.

A build tool usually results in a bundle file that is ready for distribution. One useful feature of a bundle is the ability to statically analyze code and prune unused code from the distributed bundle (sometimes called “tree-shaking”). This is useful for instance if you are referencing a huge library, but only want to use certain functions from that library. In this case the bundler will determines “dead” parts of code from the library that will never be used and removes these from your final bundle.

ECMAScript 2015

ECMAScript is a script-language specification standard for the web. New ECMAScript features are usually not immediately available in browsers. To take advantage of the new features we may use placeholders, keeping up with the latest changes will help you stay productive.

Placeholders are functions that hold an alternative, backward compatible implementation. Transpilers will run the placeholder, or polyfill, if your browser does not support it yet. As soon as the browser implements the new functionality the new implementation will automatically be taken full advantage of, with native optimizations.

While there are several tools that can help you transpile your code, we will focus on Babel. You can check out how Babel transpiles source code into native browser functions using their REPL.

Some interesting features in ES2015 include, but are not limited to:

Choosing a build tool

There are many alternatives to choose from. In this guide we will be exploring Webpack, but I encourage you to explore the options that exist out there (two good alternatives are grunt and gulp). In addition to building your project, Webpack can keep track of your dependencies, include code-splitting, and a powerful plugin system in the form of loaders.

Loaders are operations applied to files before or after they are bundled. Loaders are used for transpiling ECMAScript or other languages such as the strongly typed TypeScript, JSX used in React and other variations. In addition you can reduce total requests through inlining and minifying image assets, json data, fonts or css.

Setting up webpack is simple, provided you're starting with some npm familiarity. Let’s dive right into it and explore the features as we encounter them.

The example

We will extend a simple H5P content type called css-challenge. It can be found at github if you want to dig deeper on your own.

The content type view consists of a question and an input field. To answer the question you have to type in the correct CSS declaration, following which your css entry is applied to a target element, with the intention of making it look like the goal element.

Installing webpack

If you have npm installed and are comfortable using modules this should go fast, otherwise you might want to check up on a simple npm guide, before moving on to this part.

To install webpack, navigate to the current folder of your library using your favourite CLI [command line interface], then type:

npm init

npm install --save-dev webpack

We initialize an npm package and install webpack locally to the package as a development dependency. This gives us easy access to all of the webpack functionality from our library.

Webpack entry point

To use webpack we must have an entry point, which tells webpack what other files it will require. In the case of H5P we already have an entry point: the global variable machineName defined in your library.json. In this case, our entry point is H5P.CssChallenge. This function is defined in css-challenge.js, so we know this will be the entry point for our library.

Input.js and view.js must be required from the entry point in order to be used.

Normally we would define input and view as name-spaces under the main machineName, declaring them as global variables called H5P.CssChallenge.Input and H5P.CssChallenge.View. However, we will limit the amount of global variables we expose by requiring them locally. In order for these scripts to export themself they must be defined using the CommonJS format:

module.exports = “This string will be exported from this file”;

We only have to change a single line of code, altering "H5P.CssChallenge.Input = …” to “module.exports = …” and we are able to rid our code of excessive global variables.

Require modules

Now in order to use our newly modified modules input.js and view.js we need to require them from css-challenge.js. This is done by the require syntax:

var Input = require(‘./input’);

var View = require(‘./view’);

Bundling

There we have it, our code is completely ready to be bundled, and the simplest and fastest way to do this is through using the following CLI command:

webpack ./scripts/css-challenge.js bundle.js

This command instructs webpack to bundle the first file ./scripts/css-challenge.js and whatever requirements it may have into the second argument bundle.js. Once the command executes, we will be left with a bundle.js file.

Configuring H5P to use the bundle

Now that we have a bundle we have to tell H5P to use the bundle instead of requiring all of our JavaScript files. If you had a look at library.json at the start of this guide you should notice that it is the preloadedJs property that determines which JavaScript files are necessary to load our app. It looked like this:

 "preloadedJs": [

   {"path": "scripts/input.js"},

   {"path": "scripts/view.js"},

   {"path": "scripts/css-challenge.js"}

 ]

Since we have bundled all of these files inside the bundle.js file, we can now change preloadedJs to the following:

 "preloadedJs": [

   {"path": "bundle.js"}

 ]

Fully functioning bundle

At this point we have successfully made a complete bundle using Webpack, our build tool of choice. 

In the next guide, we will see how loaders can be used to preprocess files!