Ember Wish List

By: Brian Cardarella

It’s getting close to Christmas and I’ve got a few things on my list for Tomster Claus this year. All of my wishes are about making my applications smaller. One of the constant complaints I see about Ember is that it is “too fat”. You may not know this but this problem is solveable and can actually grow alongside Ember to ensure your assets are a slim as they can be. On to the wish list!

Tree Shaking

Are you familiar with Tree Shaking? The concept is simple, a dependency graph of your application is built. Let’s say one of your files requires A, B, and C. And A requires D, and F. And C required F. Currently with Ember CLI all files for all of your dependencies will get included in the final build. So if there is an E file it will be in the final build even if you are not using it in any way, this is wasteful. With ES6 the dependency graph can be built between your files, any files that are not in the graph are not included in the final built. They are “shaken” out of the build process. This means a smaller footprint for your assets.

There are two major hurdles to implementing this in Ember CLI right now. The first is that doing a static analysis on the dependency graph may result in false positives of what files to ignore for the build. While there are many files that you are depending upon via the import statement:

import { foo, bar } from 'baz';

This is very easy to parse. But your application can also import resources via the Ember Resolver:

container.lookup('model:foo');

A few levels down a resolveOther function is called and lookup is turned into a require:

require('my-app/models/foo');

parsing this out is not as simple. We could just assume everything in the app’s namespace should be part of the final build, but when other libraries are doing more complex tricks with importing this presents a problem. For example, in the latest version of Ember Validations the validators themselves live in the ember-validations namespace. You can override validators by placing them in your namespace. The lookup is something like this:

function lookupValidator(name) {
  return container.lookup('validator:'+name) ||
    container.lookup('ember-validations@validator:'+name);
}

How do we properly parse this out to include the correct validators in the dependency graph? One solution might be for library authors to declare which files should always be included in the final build, but this defeats the purpose of only including what is being used. If the application is using the Presence Validator but not the Inclusion Validator why would I want those extra LOCs?

The other major hurdle is Ember itself. While Ember’s source is in ES6 form the final build that you get is in AMD. Which means it is one file. Ember will have to be distributed in the original ES6 form. I am also not a fan of the current package names. If this ever happens I would much prefer:

import Component from 'ember/component';

rather than

import Component from `ember-views/views/component';

Separate builds

Ember CLI is all or nothing right now. Which means that you have a single build pipeline for your application assets (app-name.js) and a single build pipeline for 3rd party assets (vendor.js). It would be nice to define additional assets that can be built into final files. For example, this request for Ember Admin. Technically this could be done right now but it would require some heavy hacking of the vendor asset pipeline in Ember CLI. Personally I would like to see an API for this specifically. Perhaps it could be in the form of isolating a namespace to be ignored in the vendor.js final concat but still output in the dist/ directory.

Async code loading

This wish dove-tails off the previous one. Now that we have our separate assets how do we safely load them into our Ember apps? If we are isolating the assets I would think this implies they aren’t meant for consumption at application launch. Going back to the Ember Admin example, not all users need those LOCs. Only when an authorized user hits the admin functionality should it pull down the Ember Admin assets and plug into the app. This would be ideal. The major hurdle here is with how the container currently works. Perhaps something like this could put it on the right track:

resolveOther: function(name) {
  if (needAsyncLoad(name)) {
    asyncLoad(name).then(function() {
      // after this load completes the name
      // would be removed from the list of
      // resources requiring async loading
      resolveOther(name);
    }
  } else {
    return require(name);
  }
}

This would allow even further shrinking of the initial applicaiton footprint. Only include what is necessary, async load other assets. This creates the illusion of speed which is just as good as actual speed. You will have the trade-off of periodic sections of your app with a longer than normal loading state, but that should only happen once per application instance.

Wishes to reality

Fulfilling these wishes should go a long way to negating the “too fat” argument for Ember. Here’s to hoping that 2015 will see a more lean Tomster.