Ember CLI Targets 🎯

From the time that Ember CLI first started supporting transpilation of ES2015 (known as ES6 or ESNext back then), source could be authored in the latest ES version and the tooling would emit code that is ES5 compatible.

Being able to author using the current spec version but support older browsers has been awesome for developer ergonomics, but as browsers continue to catch up with the spec, that transpilation all the way back to ES5 is beginning to be a bit burdensome.

For example, lets say that you would like to use the newer for...of to iterate through an array like this:

Aside from the example being somewhat silly, the code looks nice. Now let me show you what the transpiled output is:

Hmmm, πŸ€”. This looks quite a bit different than what we had originally written, but is still going to be effectively the same when we run it. The nice part of this transpilation is that it will run on browsers as far back as IE9 (as long as you are iterating over a plain array or include something like the babel-polyfill to provide Symbol.iterator).

Now, you might be saying:

Why should I transpile for compat with IE9, I only support current evergreen browsers!?!?

I completely agree with you! 😼 What you have just discovered is that we really do not think of "transpilation targets" as specific ES versions but instead as the actual runtime browsers that we support.

For example, it seems completely reasonable for a given application to decide that they only wanted to support browsers with > 5% market share. Or maybe you'd prefer to have your apps browser support matrix be a simpler rule and set it to the latest Edge, Chrome, Firefox and Safari versions. Unfortunately, this was simply not possible to do with Ember CLI 😿.

Targets 🎯

Thankfully, Miguel Camba proposed the solution and landed the initial implementation to our dilemma. The new targets feature allows the application to do exactly what we want: list the specific browsers that we want to support, and let the tooling figure out the rest.

The targets feature added to Ember CLI provides infrastructure for plumbing the host application's desires through to the various transpilation addons so that they can make intelligent decisions and emit appropriate output. It is still up to addons to respect the projects targets, but now the information is available (and threaded through all addons). As an example of this the newest (6.0.0-beta.9) version of ember-cli-babel uses the excellent babel-preset-env preset to utilize the specified browser targets and only transpile things required by those targets. Note: In ember-cli-babel v6.0.0-beta's we have finally updated to using Babel 6 for transpilation (only a couple of years after it was released 😘).

Let's take a look at what this ends up doing for us.

Given this configuration in our config/targets.js (and using ember-cli@2.13.0-beta.1 or higher):

The original snippet from above would have the following transpiled output:

Yes, that output is exactly the same as the input 😻. Just think of the possibilities that this unlocks for us πŸ€”.

As it turns out, autoprefixer uses the same underlying database (browserlist) as babel-preset-env (no coincidence I'm sure πŸ˜‰). This allows things like autoprefixer (see kimroen/ember-cli-autoprefixer#34) to leverage your project's config/targets.js instead of requiring you to manually specify the browser list in your ember-cli-build.js file.

Or perhaps you want to leverage eslint warnings/errors when using features that are not present on your supported platforms (see ember-cli/ember-cli-eslint#158):

Or maybe leveraging our knowledge of the supported targets to determine if we need to add regenerator-runtime. This is exactly what ember-maybe-import-regenerator does for us. Hello, ember-concurrency tasks with native generator functions!

Or you can even use a custom set of targets for your development environment to be able to nicely step into/over async / await without requiring transpiling to regeneratorRuntime:

Problems πŸ˜–

Unfortunately, there are still a few issues that need to be ironed out.

Minification

One of the larger issues that the Ember CLI team is working on is determining the best path forward for JS minification. When targets configuration landed initially in ember-cli uglifyjs (which is what we have used in Ember CLI for quite a long time) did not support parsing many of the newer ES features. Thankfully, the uglify-es package has been released with support for parsing most syntaxes available in modern browsers. You will need to upgrade to ember-cli-uglify@2.0.0-beta.1.

The alternatives that we have been evaluating are closure-compiler and babili.

At this point I have not been able to get closure-compiler to emit ES2015 code (it seems to transpile ES2015 down to ES5?), and so I have started using ember-cli-uglify@2.0.0-beta.1 in projects I do not need to support things going back to IE9. Newer versions of ember-cli will update the default blueprint so things will "just work" out of the box.

CI Testing

Our old friend (or is it foe?) PhantomJS has similar issues to UglifyJS. It simply cannot parse ES2015 syntax 😭.

This means that we need to start testing in CI with either Firefox or Chrome. Thankfully, Ed Faulkner has proposed exactly this in ember-cli/rfcs#86 which is in the final comment period, and should hopefully land in Ember CLI 2.14.

While we wait for the default blueprints to be updated to use Chrome (or Firefox) we can update our testem.js (replace Phantom with Chrome) and add the following to .travis.yml:

addons:
  apt:
    packages:
     - google-chrome-stable

before_script:
  - export DISPLAY=:99; sh -e /etc/init.d/xvfb start; sleep 3

I'm sold, how do I use it?!?

  • Update your project to ember-cli@2.13 (currently in beta)
npm install --save-dev ember-cli@^2.13.0-beta.3
  • Update your version of ember-cli-babel to v6.0 (currently in beta):
npm install --save-dev ember-cli-babel@6.0.0-beta.11
  • Update your config/targets.js, something like the following would work:
module.exports = {
  browsers: [
    'last 2 Chrome versions',
    'last 2 Firefox versions',
    'last 1 Safari versions',
    'last 2 Edge versions'
  ]
};
  • Replace ember-cli-uglify with ember-cli-babili (to avoid parsing errors on newer language features):
npm uninstall --save-dev ember-cli-uglify
npm install --save-dev ember-cli-babili
  • Thats it! πŸŽ‰

This will result in your application code being transpiled directly for the targets you specified. To get target based transpilation from your addons, you will need to update them to versions that are utilizing ember-cli-babel@6.

Wrapping it up 🎁

I'm excited about the new targets feature in Ember CLI and you should be too! The ability to ship less code that more closely resembles what you authored is a win-win.

Please check it out with your application's support matrix and see how it works for you. Make sure to report any issues you come across at ember-cli/ember-cli, and feel free to ping me in slack or Twitter to chat about the possibilities!