A Hello World using Aurelia + Webpack

Aurelia + Webpack

Hello y’all! Recently I started looking into Aurelia as a replacement for Angular 1 and 2 as it seems like a very interesting framework. I still can’t say much about it as I’m just starting to play around with the framework but one of the things I like about it is the simple syntax the templates use and the fact that the ViewModels don’t require a lot of boilerplate code. After setting it up with JSPM and SystemJS, I wanted use Webpack because I use it at work and in most of my projects as I really like it, I also like to use only NPM to manage my dependencies.

Now, one thing that annoys the hell out of me in these freakin’ new frameworks is that setting them up to write a simple Hello World is not as straightforward as it used to be with older frameworks such as Angular 1, which you could simply download the JS file and reference it in the index.html. So, as you can imagine, figuring out how to set them up without using the recommended tools from their tutorials can be a bit intense. That’s why I’m writing this post, so other people don’t have to go through what I did.

Now, you must be thinking: “Dude, why the hell didn’t you simply get one of the skeleton projects and started with that?”

The reason is that I wanted to setup the project myself with the bare minimum, without setting up Karma or other extra stuff so I could understand how the framework is loaded better. Boilerplate projects are useful, but I prefer using them after I understand how the project is setup. But yeah, I had to go through the skeleton project to figure out certain things.

Now you must be thinking – “That’s cool Eric, but you talk too much, how do I do it?” – Sorry, enough blah blah blah, let’s get our hands dirty.

I’ll assume that you have played with NPM and Webpack before, so I’ll just outline the libraries we have to download. Let’s create a folder for our project, run npm init and all that basic jazz that we have to run when we start a new project.

Oh yeah, and don’t forget to install webpack globally if you don’t have it already, it’s handy.

npm install webpack -g

After doing that, we’re going to install the libraries we need:

Dev dependencies:

  • aurelia-webpack-plugin – The plugin to load Aurelia in webpack.
  • babel-core – Okay, I know I said “bare minimum” but let’s use babel to write ES6 code as it’s what Aurelia uses. But yeah, we could potentially get away with using play ES5 code.
  • babel-loader – Loader to transpile the JS files with webpack.
  • babel-preset-es2015 – The preset for ES6.
  • html-loader – This is necessary so Webpack can bundle the html templates with our javascript.
  • html-webpack-plugin – This is a cool plugin that can generate the index.html file automatically or simply add our scripts to the existing index.html automatically (among other things).
  • webpack – Duh!
  • webpack-dev-server – To run our app.
npm install aurelia-webpack-plugin babel-core babel-loader babel-preset-es2015 html-loader html-webpack-plugin webpack webpack-dev-server --save-dev

Dependencies

Alright, now that we have our dev dependencies installed, let’s install the required Aurelia dependencies:

npm install aurelia-bootstrapper-webpack aurelia-framework aurelia-event-aggregator aurelia-history-browser aurelia-loader-webpack aurelia-logging-console aurelia-templating-binding aurelia-templating-resources aurelia-templating-router --save

Remember when I said “bare minimum”? Yeah I wish we could just install aurelia-framework and aurelia-bootstrapper-webpack for a Hello World, but this is the reality of the Javascript world now.

Alright, now that we have our libraries installed, our package.json should look something like this:

{
  "name": "webpack-and-aurelia",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "You",
  "license": "ISC",
  "dependencies": {
    "aurelia-bootstrapper-webpack": "^1.0.0",
    "aurelia-event-aggregator": "^1.0.0",
    "aurelia-framework": "^1.0.1",
    "aurelia-history-browser": "^1.0.0",
    "aurelia-loader-webpack": "^1.0.2",
    "aurelia-logging-console": "^1.0.0",
    "aurelia-templating-binding": "^1.0.0",
    "aurelia-templating-resources": "^1.0.0",
    "aurelia-templating-router": "^1.0.0"
  },
  "devDependencies": {
    "aurelia-webpack-plugin": "^1.1.0",
    "babel-core": "^6.13.2",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.13.2",
    "html-loader": "^0.4.3",
    "html-webpack-plugin": "^2.22.0",
    "webpack": "^1.13.1",
    "webpack-dev-server": "^1.14.1"
  }
}

We also need a .babelrc in the root directory of our app so babel knows how to transpile our code, the file should look a bit like this:

{
    "presets": ["es2015"]
}

Cool, now, let’s create the index.html in our root directory. As you can see in the example below, we don’t have any <script> tags, those will be automatically added by html-webpack-plugin. The only extra thing we have is the aurelia-app=”main” attribute in the <body> tag which tells Aurelia what our entry point is.

Here’s how it should look like:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Aurelia and Webpack Freakin' Hello World!</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body aurelia-app="main">
    </body>
</html>

Now that we have created our index.html, create the Webpack configuration in our project’s root directory, the file should be called webpack.config.js.
Here’s what it should look like, refer to the comments to understand better what’s going on:

// The plugin that loads your source code in Aurelia.
var AureliaWebPackPlugin = require('aurelia-webpack-plugin');
// This is a node tool to resolve paths.
var path = require('path');
// We need this to use the CommonsChunkPlugin.
var webpack = require('webpack');
// The plugin that adds the script tags to our index.html
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        // Our app's entry point, this is set automatically by aurelia-webpack-plugin
        // using the files in the "src" folder that we're still going to create.
        // The app knows which of the files is our entry point because of the "aurelia-bootstrapper-webpack" 
        // who reads the entry point from the aurelia-app="main" in our index.html.
        'app': [],
        //These are all the aurelia libraries, they will be bundled separately from our app's main code.
        //I tried to bundle everything together, with no success.
        'aurelia': [
            'aurelia-bootstrapper-webpack',
            'aurelia-polyfills',
            'aurelia-pal',
            'aurelia-pal-browser',
            'aurelia-binding',
            'aurelia-dependency-injection',
            'aurelia-event-aggregator',
            'aurelia-framework',
            'aurelia-history',
            'aurelia-history-browser',
            'aurelia-loader',
            'aurelia-loader-webpack',
            'aurelia-logging',
            'aurelia-logging-console',
            'aurelia-metadata',
            'aurelia-path',
            'aurelia-route-recognizer',
            'aurelia-router',
            'aurelia-task-queue',
            'aurelia-templating',
            'aurelia-templating-binding',
            'aurelia-templating-router',
            'aurelia-templating-resources'
        ]
    },
    output: {
        //This is the folder where our packed app will be after we run webpack.
        path: './build',
        filename: 'scripts/[name].bundle.js',
        sourceMapFilename: 'scripts/[name].bundle.js.map'
    },
    module: {
        loaders: [
            //This loader runs babel for every js file so the files are transpiled to ES5 javascript.
            { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" },
            //This loader reads our html templates that are referenced and bundles them with our javascript.
            { test: /\.html$/, loader:'html', exclude: path.resolve('src/index.html') }
        ]
    },
    plugins: [
        //The Aurelia Plugin.
        new AureliaWebPackPlugin(),
        // This is what will create a separate bundle for the libs under 'aurelia' 
        // in our entry section.
        new webpack.optimize.CommonsChunkPlugin({ name: ['aurelia']}),
        // This plugin will add our bundles to the html file and copy it 
        // to the build folder.
        new HtmlWebpackPlugin({
            template: 'index.html',
            chunksSortMode: 'dependency'
        }),
    ],
    // This is not necessary, it just changes the default port the
    // webpack-dev-server uses from 3000 to whatever you set here.
    devServer: {
        port: 3333
    }
};

Alright! Now that we have our base infrastructure, let’s set camp and continue tomorrow…
Oh, you want to finish this now? Alright then… Let’s do it!

In our project’s root folder, let’s create a src folder, where our code will live in. Inside the src folder, let’s create a file called main.js and also an app folder and inside this folder let’s create two files: app.js and app.html. Here’s what those files do:

src/main.js

This file is the entry point of our app, it’s the file we referenced in our index.html. It will start Aurelia and set our root to be our app.js file.

export function configure(aurelia) {
    aurelia.use
        .standardConfiguration()
        .developmentLogging();

    aurelia.start().then(a => { 
        //this loads our app.js in the body element.
        a.setRoot('app/app', document.body);
    });
}

[Update 19/Aug/2016] You can actually load Aurelia with a more basic set of plugins by using .basicConfiguration() instead of .standardConfiguration().

src/app/app.js

This is our main view model, the only thing it contains is a property called message that will be bound to our view.

export class App {
    constructor() {
        this.message = 'some text you can change!';
    }
}

src/app/app.html

This is the view for our App view model. It’s just a simple Aurelia template.

<template>
    <h1>Hello Freakin' world.</h1>
    <div>${message}</div>
    <input type="text" value.bind="message">
</template>

Coolio! Our setup is done! After doing all that, our folder structure should look like this:

Aurelia + Webpack folder structure.

Now, if you run the webpack command in the terminal, a build folder should be created in the root directory with our bundles and the index.html.

To test our app we will run the webpack-dev-server in the terminal and access http://localhost:3333. The loaded page should look like this:

Aurelia + Webpack Hello World Page

As you might have noticed our binding is working so, if you edit the text in the textbox, the text in the text div will be updated accordingly.

And that’s it! After figuring out how to setup everything it’s actually not too hard, but the problem is figuring it out in the first place as there aren’t many resources on this on the Internet and, because Aurelia uses convention over configuration, many things happen implicitly.

Here’s the source code on GitHub.

In the next post I will take this project to the next level by adding support to Typescript. Until then, have a good one!

Eric Mackrodt on sabyoutubeEric Mackrodt on sabtwitterEric Mackrodt on sablinkedinEric Mackrodt on sabgithubEric Mackrodt on sabemail
Eric Mackrodt
Software Developer, Tech Enthusiast, Music Lover, Movie Watcher, TV Junkie, food eater, air breather...
Back To Top