By Oleksii Rudenko March 5, 2015 11:06 PM
Custom Build Workflows Using Ember and JSPM

tl; dr…

Loading Ember Templates with JSPM, Ember 1.11 and Gulp

JSPM is a cool browser package manager which allows not only loading all possible JS module formats from Github, NPM and custom endpoints but also supports ES6 feautres and in particular ES6 loader out of the box.

This blog post will be helpful for those who don’t use ember-cli and manage their own build systems. I will explain how to set up jspm for an Ember project and how to leverage ES6 loader for your Handlebars templates.

Setting up package.json for your project

So let’s create the following package.json in your app folder:

{
  "name": "jspm-ember",
  "version": "0.1.0",
  "description": "Demonstration of Ember 1.11 and JSPM",
  "homepage": "",
  "repository": {
    "type": "git",
    "url": ""
  },
  "devDependencies": {
    "browser-sync": "^1.6.3",
    "gulp": "^3.8.11",
    "jspm": "^0.14.0"
  },
  "jspm": {
    "directories": {
      "baseURL": "app",
      "lib": "app"
    },
    "dependencies": {
      "ember": "github:components/ember@1.11.0-beta.2",
      "hbs": "github:n-fuse/plugin-ember-hbs@1.11.0-beta.2",
      "ihbs": "github:n-fuse/plugin-ember-ihbs@1.11.0-beta.2",
      "jquery": "github:components/jquery@^2.0.3"
    }
  }
}

It defines the following devDependencies:

  • browser-sync - provides local web server
  • gulp - the build system that I prefer
  • jspm - the JSPM

Run npm install and you’ll have these packages installed. Additionally, we’d need jspm-cli so install it using npm install jspm -g.

The jspm key in the package.json defines a configuration for jspm and in particular it defines packages the app will need. In this case, we require the ihbs and hbs modules which are hosted on Github. We need them to be able to load hbs templates as JavaScript modules.

The Ember package requires some more configuration. Let’s create a folder called overrides and put there ember.json:

{
  "main": "ember.debug",
  "files": ["ember-template-compiler.js", "ember.debug.js"],
  "dependencies": {
    "jquery": "^2.1.3"
  },
  "shim": {
    "ember.debug": {
      "deps": ["jquery"],
      "exports": "Ember"
    }
  }
}

This file will override the package.json of the Ember package and instead of loading whatever is loaded in the original Ember package it will load ember-template-compiler.js and ember.debug.js only. Also it defines that the Ember object from the package will be exported by default.

Now we can fully install Ember.js:

jspm init
jspm install && jspm install github:components/ember@1.11.0-beta.2 -o overrides/ember.json

jspm install will install all dependencies and jspm install github:components/ember@1.11.0-beta.2 -o overrides/ember.json will install Ember with the specified override.

Gulpfile

Let’s use this Gulp file:

var gulp = require('gulp');
var jspm = require('jspm');

// watch files for changes and reload
gulp.task('watch', function() {
  var browserSync = require('browser-sync');
  browserSync({
    online: false,
    server: {
      baseDir: ['app']
    }
  });

  gulp.watch([
    'app/**/*.*'
  ], function(file){
    browserSync.reload(file.path);
  });
});

gulp.task('jspm', function() {
  return jspm.bundleSFX('main', 'dist/app.js', { sourceMaps: false });
});

The file defines two tasks: watch and jspm. Watch will start the local webserver and jspm which will create a production bundle of our application code with all dependencies.

The App

In the app folder, let’s create app.js and index.html:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Ember JSPM Demo</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <script src="jspm_packages/system.js"></script>
  <script src="config.js"></script>
  <script>
    System.baseURL = '/';
    System.import('app');
  </script>
</head>
<body>
</body>
</html>
// app.js

import Ember from 'ember';

import layout from 'templates/component-layout.hbs!';

var MyComponent = Ember.Component.extend({
  layout: layout
});

Ember.Handlebars.helper('my-component', MyComponent);

import tmpl from 'templates/application.hbs!ihbs';
var Demo = Ember.Application.create();

Let’s create templates folder and put application.hbs and component-layout.hbs in there:

Application Template {{outlet}}
{{my-component}}


```hbs
This is component

Run gulp watch now and you’ll we see the working app:

ihbs and hbs modules

These are two modules responsible for the template loading. Actually, they are SystemJS plugins and SystemJS is used by JSPM to implement transparent import across various formats.

You can check them out here: plugin-ember-hbs and here plugin-ember-ihbs

The plugins define a translate hook for the ES loader:


import EmTC from 'ember-template-compiler';

export var translate = function(load) {
  console.log(`Compiling template: ${load.metadata.pluginArgument}`);
  var template = EmTC.precompile(load.source, false);
  return `import Em from 'ember';\n
  export default Em.HTMLBars.template(${template});`;
};

In the hook, ember-template-compiler is used to precompile a template and to export it as a JS modile. After this plugin is installed in your app, you can import templates as following:

import tmpl from 'templates/tmpl.hbs!';

This will import tmpl.hbs from the templates folder and make it available in your code as tmpl variable.

Thanks for reading.