Chris Ball
Managing Partner
September 26, 2016

ember version badge

Do you find yourself making the same set of changes to a file every time you run ember g <blueprint-name>? Did you know you can customize Ember’s built-in blueprints to fit the style of your project and avoid the extra work?

What are blueprints?

Blueprints are what ember-cli uses to generate files when running ember generate.

It’s a good idea to use the generators rather than manually creating files in your editor. That way, related files like tests get created and you’ll always be using the latest recommended syntax. To see all of the available blueprints type ember g --help.

Adding your own blueprints

ember-cli ships with quite a few blueprints, but you can also create your own in addons or your application.

One of the built-in blueprints generates a blueprint (how meta!), which you can use by typing ember g blueprint my-blueprint.

Improving the built-in route blueprint

Let’s examine the built-in route blueprint. Here’s what Ember gives us out of the box when we generate a route:

→ ember g route taco

installing route
  create app/routes/taco.js
  create app/templates/taco.hbs
updating router
  add route taco
installing route-test
  create tests/unit/routes/taco-test.js
// app/routes/taco.js
import Ember from 'ember';

export default Ember.Route.extend({
});

This is a great starting point, but as your project evolves you will notice patterns that emerge in how you write each of your files. Here are three of the many possible scenarios.

Scenario 1: You use Ember Simple Auth and you forget to import the AuthenticatedRoute mixin.

If you use Ember Simple Auth, most of your route files probably look like this:

import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
});

Most new routes should be authenticated routes, and you have to remember to copy and paste the mixin every time. It’s tedious and error prone.

Scenario 2: You are required by your Legal department to add a copyright to every file.

/**
 * © 2016 My Company
 * Man bun wayfarers plaid, cornhole letterpress meh selfies organic. PBR&B
 * gentrify viral deep v, food truck hexagon etsy narwhal hot chicken
 * cold-pressed shoreditch pour-over synth biodiesel.
 */
export default Ember.Route.extend({
});

If this scenario sounds familiar, you’re probably tired of “Just add the Legal blurb then :shipit:” feedback on your Pull Requests.

Scenario 3: You have linting rules that don’t like it when you use Ember.Route.extend.

It’s common to have linting rules that disallow direct property access, so instead of Ember.Route.extend, you set Route to a const and then call Route.extend.

import Ember from 'ember';

const { Route } = Ember;

export default Route.extend({
});

Why force yourself to make this change to every file after you generate it?

Customizing built-in blueprints

A better way to approach these types of scenarios is to customize Ember’s built-in blueprints. How? Override the necessary files and extend it. Any blueprint in your app namespace will always take precedence over the built-in blueprints.

There are two places to look to find the default blueprints:

Once Ember has fully migrated to NPM, the Ember specific blueprints will live in the Ember addon (hence the name legacy-blueprints). The rest of this article will assume ember-cli 2.5+.

Find the necessary files

Let’s say we want to override the route blueprint. We can find it here.

The generated JavaScript file is located at ember-cli-legacy-blueprints/blueprints/route/files/__root__/__path__/__name__.js. All we have to do is create a version under our app namespace:

// blueprints/route/files/__root__/__path__/__name__.js
/**
 * © 2016 My Company
 * Man bun wayfarers plaid, cornhole letterpress meh selfies organic. PBR&B
 * gentrify viral deep v, food truck hexagon etsy narwhal hot chicken
 * cold-pressed shoreditch pour-over synth biodiesel.
 */
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

const { Route } = Ember;

export default Route.extend(AuthenticatedRouteMixin, {
});

Now, when we run ember g route taco, we should get the contents of the file above. Cool stuff!

You might notice that ember does not generate a template, but the built-in version of the route blueprint does. To fix that, we’ll start by creating the template the same way we created the js file above:

{{!-- blueprints/route/files/__root__/__templatepath__/__templatename__.hbs --}}
{{outlet}}

If we run ember g route taco now, we’ll notice that the templatepath and templatename variables are not working properly:

→ ember g route taco

installing route
  create app/__templatepath__/__templatename__.hbs
installing route-test
  identical tests/unit/routes/taco-test.js

To make this work properly, we also need to extend the index.js file for the route blueprint where the templatename and templatepath variables are defined (ember-cli-legacy-blueprints/blueprints/route/index.js).

NOTE: Blueprints run in Node, so don’t forget to use require instead of import.

// blueprints/route/index.js
/*jshint node:true*/
var RouteBlueprint = require('ember-cli-legacy-blueprints/blueprints/route/index');

module.exports = RouteBlueprint;

Let’s generate our route again.

→ ember g route taco

installing route
  create app/templates/taco.hbs
updating router
  add route taco
installing route-test
  identical tests/unit/routes/taco-test.js

Success

We’ve successfully reduced manual process and the amount of typing needed when creating new routes. When common patterns emerge, override the necessary blueprints in your app. If your company happens to have multiple Ember apps, it is a good idea to create these blueprints in a shared addon to help maintain consistency between them.

Want to level-up your Ember app and your team? We’d love to help.