This is a version of another post I wrote in 2016, but using webpack instead of gulp

In this post, I’m going to walk through creating a very simple Javascript project. The project itself is not going to do anything much, but the point (as always, with javascript) is to figure out how to get the build pipeline set up correctly. I’m going to try to use a minimum of components, and in the end I want the following;

  • A static javascript application, deliverable as two files;
    • index.html
    • application.js

The application should work in all modern browsers, and should have its own stylesheet.

  • When working on the site, I want to;
    • write my javascript using ES2015
    • keep my javascript classes in separate files

For the purposes of this exercise, all the application needs to do is write a message into a web page. The point is to use ES2015 to do that.

Getting started

Let’s start by setting up a fresh project;

$ mkdir hello-es2015
$ cd hello-es2015
$ git init

Let’s add some directories

$ mkdir src dist
$ touch src/.gitkeep dist/.gitkeep

We’re going to be adding some npm packages, but we don’t want them in our git repo.

$ echo node_modules/ > .gitignore

Finally, we’ll need an empty package.json file;

{
}

Now let’s add some source files;

src/app.js

console.log("Hello from app.js")

src/stylesheet.css

body {
  background-color:  black;
  font-family:       "Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;
  color:             white;
  font-weight:       bold;
  margin:            20px 20px auto;
}

main {
  width: 95%;
  margin: auto;
}

src/index.html

<html>
  <head>
    <meta name="viewport" content="width=500,initial-scale=1.0,user-scalable=yes">
    <title>Hello ES2015</title>
  </head>
  <body>
    <main>
      <p>This is not very interesting</p>
    </main>
  </body>
</html>

Let’s commit what we’ve got, so far

$ git add .
$ git commit -m "Initial commit"

All very simple. If you open src/index.html in your browser, you should see an uninspiring web page with no styling and with nothing logged to the console.

Webpack

We’re going to need a build tool to take our separate javascript classes and combine them. We’re also going to need to transpile the ES2015 javascript into the ES5 code supported by the majority of browsers. So, let’s create a build system to generate our dist/ files.

We’re going to use webpack, so first we need to install it as a dependency;

$ npm install webpack --save-dev

The default configuration file for webpack is webpack.config.js so let’s create that now;

var path = require('path')

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

This says that src/app.js is the entrypoint of the source code for our application, and that we want the output bundle to be called application.js in the dist/ folder. Now, when you run webpack, you should see output like this;

$ ./node_modules/webpack/bin/webpack.js

Hash: ea4ea2ad384024eb4abd
Version: webpack 2.2.1
Time: 66ms
         Asset     Size  Chunks             Chunk Names
application.js  2.54 kB       0  [emitted]  main
   [0] ./src/app.js 33 bytes {0} [built]

If you look in the dist folder, you should have an application.js file.

Creating the index.html file

We need webpack to create the index.html in our dist folder. To do that, we need to install a webpack plugin;

npm install html-webpack-plugin --save-dev

By default, the html plugin will generate an index.html file from scratch, but I want to use our src/index.html file as a template. Change your webpack.config.js file to look like this;

var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'application.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [new HtmlWebpackPlugin({ template: 'src/index.html' })]
}

Now, after you run webpack your dist folder should contain both an application.js file and an index.html file. If you open up the index.html file in a browser, you should see the web page, and some output on the javascript console.

Adding CSS styles

The page doesn’t have our styling, though. To fix that, we need to add a couple of webpack loaders so that webpack knows how to take our CSS file and bundle the styling into the dist/application.js file.

npm install --save-dev css-loader
npm install --save-dev style-loader

To use these, we need to add a module entry to our webpack.config.jsfile, like this;

var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'application.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin({ template: 'src/index.html' })]
}

Webpack won’t build anything we don’t need, so we need to tell it that we want it to include our CSS. We can do that by modifying src/app.js like this;

import css from './stylesheet.css'

console.log("Hello from app.js")

Now, when you run webpack, you should still have just the two files in dist, but webpack will have embedded the styles from src/stylesheet.css in the application.js file. Load up dist/index.html and it should have white text on a black background.

ES2015

Webpack handles ES2015 javascript, but it won’t transpile it to ES5 until we tell it to do that. We can prove that by using some ES2015 features in our javascript.

First, add a src/hello.js file;

class Hello {
  constructor(config) {
    this.target = config.target;
  }

  run() {
    this.target.innerHTML = `
      <p>
        Hello from ES2015
      </p>
    `;
  }
}

export default Hello

Edit src/app.js;

import css from './stylesheet.css'
import Hello from './hello'

(new Hello({
  target: document.getElementsByTagName('main')[0]
})).run()

After running webpack, when we load dist/index.html, we should see “Hello from ES2015” So, we know our javascript is working.

If you open up dist/application.js and look for ‘Hello’, you should see something like this;

/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
class Hello {
  constructor(config) {
    this.target = config.target;
  }

  run() {
    this.target.innerHTML = `
      <p>
        Hello from ES2015
      </p>
    `;
  }
}

/* harmony default export */ __webpack_exports__["a"] = Hello;

That’s ES2015 javascript. We want webpack to create ES5 javascript, so it will run in older browsers. To do that, we need to use babel with the es2015 preset;

npm install babel-core --save-dev
npm install babel-loader --save-dev
npm install babel-preset-es2015 --save-dev

We need to add a stanza to our webpack.config.js so that all .js files get transpiled;

var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'application.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin({ template: 'src/index.html' })]
}

Now, after running webpack and loading dist/index.html in the browser, our application should still work as before. But, if you open up dist/application.js and search for ‘Hello’, you should see this;

var Hello = function () {
  function Hello(config) {
    _classCallCheck(this, Hello);

    this.target = config.target;
  }

  _createClass(Hello, [{
    key: "run",
    value: function run() {
      this.target.innerHTML = "\n      <p>\n        Hello from ES2015\n      </p>\n    ";
    }
  }]);

  return Hello;
}();

exports.default = Hello;

The source code for this blog post is available here

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s