Getting Started
Prerequisites
Node
First, install the latest LTS (long-term support) version of Node.
Node is available for a variety of platforms at
nodejs.org. It is important that you not install Node
with sudo
in order to avoid permission problems with some ember-cli commands.
On Unix, nvm provides a convenient way to do this. On OS X, you can also use
Homebrew.
On Windows, chocolatey is an
option, or with Windows Subsystem for Linux (WSL) you can use nvm
or linuxbrew.sh.
After the installation is complete, verify that Node is set up correctly by typing the below commands on the command line. Both should output a version number:
node -v
npm -v
Ember CLI
Once you’ve installed Node, you’ll need to globally install Ember CLI:
npm install -g ember-cli
This will give you access to the ember
command-line runner.
Watchman
On OSX and UNIX-like operating systems, we recommend installing Watchman version >= 4.x, which provides Ember CLI a more effective way for watching project changes.
File-watching on OSX is error-prone and Node’s built-in NodeWatcher
has
trouble observing large trees. Watchman
on the other hand, solves these problems and performs well on extremely massive
file trees. You can read more about Facebook’s motivations
here.
On OSX, you can install Watchman using Homebrew:
brew install watchman
For complete installation instructions, refer to the docs on the Watchman
website. Note, there exists a similarly
named npm
package (watchman) which is not the intended installation. If
you have this package installed you may see the following warning:
invalid watchman found, version: [2.9.8] did not satisfy [^3.0.0], falling back to NodeWatcher
If you intend on using the npm
version for another purpose, make sure it’s
not on your PATH
otherwise, remove it using:
npm uninstall -g watchman
When in doubt, use the following command to inspect which Watchman(s) you have:
which -a watchman
Lastly, when Watchman is not installed, a notice is displayed when invoking various commands. You can safely ignore this message:
Could not find watchman, falling back to NodeWatcher for file system events
Create a new project
Run the new
command along with the desired app name to create a new project:
ember new my-new-app
Ember CLI will create a new my-new-app
directory and in it, generate the application structure.
Once the generation process is complete, launch the app:
cd my-new-app
ember server
Navigate to http://localhost:4200
to see your new app in action.
Navigate to http://localhost:4200/tests
to see your test results in action.
Migrating an existing Ember project that doesn’t use Ember CLI
If your app uses the deprecated Ember App Kit, there is a migration guide located on the README.
If your app uses globals (e.g. App.Post
) from a different build pipeline such
as Grunt, Ember-Rails, or Gulp, you can try using the Ember CLI
migrator. The Ember CLI
migrator is a command line tool that looks at your JavaScript code using a
JavaScript parser and rewrites it to ES6 following Ember CLI’s conventions. The
migrator keeps your code style and keeps git history available via git log
--follow
.
Cloning an existing project
If you are checking out an existing Ember CLI-based project, you will need to
install npm
dependencies:
git clone [email protected]:me/my-app.git
cd my-app
npm install
bower install
You might have to additionally install bower
dependencies if the project
includes a bower.json
file:
bower install
Using Ember CLI
Command | Purpose |
---|---|
ember |
Prints out a list of available commands. |
ember new <app-name> |
Creates a directory called <app-name> and in it, generates an application structure. If git is available the directory will be initialized as a git repository and an initial commit will be created. Use --skip-git flag to disable this feature. |
ember init |
Generates an application structure in the current directory. |
ember build |
Builds the application into the dist/ directory (customize via the --output-path flag). Use the --environment flag to specify the build environment (defaults to development ). Use the --watch flag to keep the process running and rebuilding when changes occur. |
ember server |
Starts the server. The default port is 4200 . Use the --proxy flag to proxy all ajax requests to the given address. For example, ember server --proxy http://127.0.0.1:8080 will proxy all ajax requests to the server running at http://127.0.0.1:8080 . Aliases: ember s , ember serve |
ember generate <generator-name> <options> |
Runs a specific generator. To see available generators, run ember help generate . Alias: ember g |
ember destroy <generator-name> <options> |
Removes code created by the generate command. If the code was generated with the --pod flag, you must use the same flag when running the destroy command. Alias: ember d |
ember test |
Run tests with Testem in CI mode. You can pass any options to Testem through a testem.json file. By default, Ember CLI will search for it under your project’s root. Alternatively, you can specify a config-file . Alias: ember t |
ember install <addon-name> |
Installs the given addon into your project and saves it to the package.json file. If provided, the command will run the addon’s default blueprint. |
Folder Layout
File/directory | Purpose |
---|---|
app/ |
Contains your Ember application’s code. Javascript files in this directory are compiled through the ES6 module transpiler and concatenated into a file called <app-name>.js . See the table below for more details. |
dist/ |
Contains the distributable (optimized and self-contained) output of your application. Deploy this to your server! |
public/ |
This directory will be copied verbatim into the root of your built application. Use this for assets that don’t have a build step, such as images or fonts. |
tests/ |
Includes your app’s unit and integration tests, as well as various helpers to load and run the tests. |
tmp/ |
Temporary application build-step and debug output. |
node_modules/ |
npm dependencies (both default and user-installed). |
vendor/ |
Your external dependencies not installed with Bower or npm . |
.eslintrc.js |
ESLint configuration. |
.gitignore |
Git configuration for ignored files. |
ember-cli-build.js |
Contains the build specification for Broccoli. |
package.json |
npm configuration and dependency list. Mainly used to list the dependencies needed for asset compilation. |
Layout within app
directory
File/directory | Purpose |
---|---|
app/app.js |
Your application’s entry point. This is the first executed module. |
app/index.html |
The only page of your single-page app! Includes dependencies, and kickstarts your Ember application. See app/index.html. |
app/router.js |
Your route configuration. The routes defined here correspond to routes in app/routes/ . |
app/styles/ |
Contains your stylesheets, whether SASS, LESS, Stylus, Compass, or plain CSS (though only one type is allowed, see Asset Compilation). These are all compiled into <app-name>.css . |
app/templates/ |
Your HTMLBars templates. These are compiled to /dist/assets/<app-name>.js . The templates are named the same as their filename, minus the extension (i.e. templates/foo/bar.hbs -> foo/bar ). |
app/controllers/ , app/models/ , etc. |
Modules resolved by the Ember CLI resolver. See Using Modules & the Resolver. |
app/index.html
The app/index.html
file lays the foundation for your application. This is where the basic DOM structure is laid out, the title attribute is set, and stylesheet/javascript includes are done. In addition to this, app/index.html
includes multiple hooks - {{content-for 'head'}}
and {{content-for 'body'}}
- that can be used by addons to inject content into your application’s head
or body
. These hooks need to be left in place for your application to function properly however, they can be safely ignored unless you are directly working with a particular addon.
Addons
Addons are registered in npm with a keyword of ember-addon
. You can see a
list of existing addons directly on npm,
on emberaddons.com, or a curated and
categorized listing at Ember Observer.
Developing on a subpath
If your app isn’t running on the root URL (/
), but on a subpath (like
/my-app/
), your app will only be accessible on /my-app/
and not on
/my-app
, too. Sometimes, this can be a bit annoying. Therefore you should
take the following snippet as an example for a simple route which takes you to
the right URL if you’ve entered the wrong one:
// index.js
module.exports = function(app) {
app.get('/my-app', function(req, res, next) {
let subpath = '/my-app/';
if (req.path !== subpath) {
res.redirect(subpath);
} else {
next();
}
});
};
Just place it within the index.js
file of your app’s /server
directory (so that it gets applied when the ember-cli development server is being started). The snippet simply tests if the URL that is being accessed begins with /my-app/
. And if it doesn’t, you’ll get redirected. Otherwise, the redirection will be skipped.
Using Modules & the Resolver
The Ember Resolver is the mechanism responsible for looking up code in your application and converting its naming conventions into the actual classes, functions, and templates that Ember needs to resolve its dependencies, for example, what template to render for a given route. For an introduction to the Ember Resolver, and a basic example of how it actually works, see this video by Robert Jackson.
In the past, Ember’s Default Resolver worked by putting everything into a global namespace, so you will come across the following pattern:
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
Today, Ember CLI uses a newer version of the Resolver based on ES6 semantics. This means that you can build your apps using syntax from future JavaScript versions, but output AMD modules that can be used by existing JavaScript libraries today.
For example, this route definition in app/routes/index.js
would result in a
module called your-app/routes/index
. Using the resolver, when Ember looks up
the index route, it will find this module and use the object that it exports.
// app/routes/index.js
import Route from '@ember/routing/route';
export default Route.extend({
model() {
return ['red', 'yellow', 'blue'];
}
});
You can also require modules directly with the following syntax:
import FooMixin from './mixins/foo';
You can reference a module by using either a relative or absolute path.
If you would like to reference a module using absolute begin
the path with the app name defined in package.json
:
import FooMixin from 'my-app/mixins/foo';
Similarly, you can give any name to the variable into which you import a module
when doing so manually; see how the module mixins/foo
is assigned to variable
FooMixin
in the example above.
Using Ember or Ember Data
To use Ember
or DS
(for Ember Data) in your modules you must import them:
import Ember from 'ember';
import DS from 'ember-data';
Using Pods
One of the enhancements that the new Resolver brings is that it will first look for Pods before the traditional project structure.
Module Directory Naming Structure
Directory | Purpose |
---|---|
app/adapters/ |
Adapters with the convention adapter-name.js . |
app/components/ |
Components with the convention component-name.js . Components must have a dash in their name. So blog-post is an acceptable name, but post is not. |
app/helpers/ |
Helpers with the convention helper-name.js . Helpers must have a dash in their name. Remember that you must register your helpers by exporting makeBoundHelper or calling registerBoundHelper explicitly. |
app/instance-initializers/ |
Initializers with the convention initializer-name.js . Initializers are loaded automatically. |
app/mixins/ |
Mixins with the convention mixin-name.js . |
app/models/ |
Models with the convention model-name.js . |
app/routes/ |
Routes with the convention route-name.js . Child routes are defined in sub-directories, parent/child.js . To provide a custom implementation for generated routes (equivalent to App.Route when using globals), use app/routes/basic.js . |
app/serializers/ |
Serializers for your models or adapter, where model-name.js or adapter-name.js . |
app/transforms/ |
Transforms for custom Ember Data attributes, where attribute-name.js is the new attribute. |
app/utils/ |
Utility modules with the convention utility-name.js . |
All modules in the app
directory can be loaded by the resolver but typically
classes such as mixins
and utils
should be loaded manually with an import statement.
For more information, see Naming Conventions.
Resolving Handlebars helpers
Custom Handlebars helpers are one of the ways that you can use the same HTML
multiple times in your web application. Registering your custom helper allows
it to be invoked from any of your Handlebars templates. Custom helpers are
located under app/helpers
.
// app/helpers/upper-case.js
import { helper } from '@ember/component/helper';
export default helper(function([value]) {
return value.toUpperCase();
});
In some-template.hbs
:
{{upper-case "foo"}}
While previous versions of ember-cli required auto-resolved helpers only if they contain a dash, now all helpers are auto-resolved, regardless of whether they contain a dash or not.
A common pattern with helpers is to define a helper to use your views (e.g. for
a custom text field view, MyTextField
a helper my-text-field
to use it). It
is advised to leverage Components instead.
Do this:
// Given... app/components/my-text-field.js
import TextField from '@ember/component/text-field';
export default TextField.extend({
// some custom behaviour...
});
Using global variables or external scripts
If you want to use external libraries that write to a global namespace (e.g.
moment.js), you need to add those to the globals
section of your project’s .eslintrc.js
file and set its value to true. If you
use the lib in tests, you need to add it to your tests/.eslintrc.js
file, too.
Naming Conventions
Overview
kebab-case
- file names
- directory names
- html tags/ember components
- CSS classes
- URLs
camelCase
- JavaScript
- JSON
When using Ember CLI it’s important to keep in mind that the Resolver changes some of the naming conventions you would typically use out of the box with Ember, Ember Data and Handlebars. In this section we review some of these naming conventions.
Module Examples
Adapters
// app/adapters/application.js
import DS from 'ember-data';
const { RESTAdapter } = DS;
export default RESTAdapter.extend({});
Components
// app/components/time-input.js
import TextField from '@ember/component/text-field';
export default TextField.extend({});
Controllers
// app/controllers/stop-watch.js
import Controller from '@ember/controller';
export default Controller.extend({});
And if it’s a nested controller, we can declare nested/child controllers
like such: app/controllers/posts/index.js
.
Helpers
// app/helpers/format-time.js
import { helper } from '@ember/component/helper';
export default helper(function(){});
Instance Initializers
// app/instance-initializers/observation.js
export function initialize(appInstance) {
};
export default {
name: 'observation',
initialize: initialize
};
Note: instance-initializers
are loaded automatically.
Mixins
// app/mixins/evented.js
import Mixin from '@ember/object/mixin';
export default Mixin.create({});
Models
// app/models/observation.js
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({});
Routes
// app/routes/timer.js
import Route from '@ember/routing/route';
export default Route.extend({});
Nested routes as such: app/routes/timer/index.js
or app/routes/timer/record.js
.
Serializers
// app/serializers/observation.js
import DS from 'ember-data';
const { RESTSerializer } = DS;
export default RESTSerializer.extend({});
Transforms
// app/transforms/time.js
import DS from 'ember-data';
const { Transform } = DS;
export default Transform.extend({});
Utilities
// app/utils/my-ajax.js
export default function myAjax() {};
Views
<!-- app/index.hbs -->
{{view 'stop-watch'}}
// app/views/stop-watch.js
import Ember from 'ember';
export default Ember.View.extend({});
And views, which can be referenced in sub-directories, but have no inheritance.
<!-- app/index.hbs -->
{{view 'inputs/time-input'}}
// app/views/inputs/time-input.js
import TextField from '@ember/component/text-field';
export default TextField.extend({});
Filenames
It is important to keep in mind that the Resolver uses filenames to create the associations correctly. This helps you by not having to namespace everything yourself. But there are a couple of things you should know.
All filenames should be lowercased
// models/user.js
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({});
Dasherized file and directory names are required
// controllers/sign-up.js
import Controller from '@ember/controller';
export default Controller.extend({});
Nested directories
If you prefer to nest your files to better manage your application, you can easily do so.
// controllers/posts/new.js results in a controller named "controllers.posts/new"
import Controller from '@ember/controller';
export default Controller.extend({});
You cannot use paths containing slashes in your templates because Handlebars will translate them back to dots. Simply create an alias like this:
// controllers/posts.js
import Controller from '@ember/controller';
import { alias } from '@ember/object/computed';
export default Controller.extend({
needs: ['posts/details'],
postsDetails: alias('controllers.posts/details')
});
<!-- templates/posts.hbs -->
<!-- because {{controllers.posts/details.count}} does not work -->
{{postsDetails.count}}
Tests
Test filenames should be suffixed with -test.js
in order to run.
Pod structure
As your app gets bigger, a feature-driven structure may be better. Splitting your application by functionality/resource would give you more power and control to scale and maintain it. As a default, if the file is not found on the POD structure, the Resolver will look it up within the normal structure.
In this case, you should name the file as its functionality. Given a resource
Users
, the directory structure would be:
app/users/controller.js
app/users/route.js
app/users/template.hbs
Rather than hold your resource directories on the root of your app you can define a POD path using the attribute podModulePrefix
within your environment configs. The POD path should use the following format: {appname}/{poddir}
.
// config/environment.js
module.exports = function(environment) {
let ENV = {
modulePrefix: 'my-new-app',
// namespaced directory where resolver will look for your resource files
podModulePrefix: 'my-new-app/pods',
environment: environment,
rootURL: '/',
locationType: 'auto'
//...
};
return ENV;
};
Then your directory structure would be:
app/pods/users/controller.js
app/pods/users/route.js
app/pods/users/template.hbs
Using With Ember Data
The current version of Ember Data is included with Ember CLI.
Ember Data has undergone some major reboots, drastically simplifying it and making it easier to use with the Ember resolver. Here’s some tips for using it within Ember CLI.
To use ember-cli without Ember Data remove the dependency from package.json (the same applies for ic-ajax)
npm rm ember-data --save-dev
Models
Models are critical in any dynamic web application. Ember Data makes making models extremely easy.
For example, we can create a todo
model like so:
// models/todo.js
import DS from 'ember-data';
const { Model, attr, hasMany } = DS;
export default Model.extend({
title: attr('string'),
isCompleted: attr('boolean'),
quickNotes: hasMany('quick-note')
});
// models/quick-note.js
import DS from 'ember-data';
const { Model, attr, belongsTo } = DS;
export default Model.extend({
name: attr('string'),
todo: belongsTo('todo')
});
Note, that filenames should be all lowercase and dasherized - this is used by the Resolver automatically.
Adapters & Serializers
Ember Data makes heavy use of per-type adapters and serializers. These objects can be resolved like any other.
Adapters can be placed at /app/adapters/type.js
:
// adapters/post.js
import DS from 'ember-data';
const { RESTAdapter } = DS;
export default RESTAdapter.extend({});
And its serializer can be placed in /app/serializers/type.js
:
// serializers/post.js
import DS from 'ember-data';
const { RESTSerializer } = DS;
export default RESTSerializer.extend({});
Application-level (default) adapters and serializers should be named
adapters/application.js
and serializers/application.js
, respectively.
Mocks
Ember CLI comes with an http-mock generator which is preferred to fixtures for development. Mocks have several advantages over fixtures, a primary one being that they interact with your application’s adapters. Since you’ll eventually be hooking your app up to a live API, it’s wise to be testing your adapters from the onset.
To create a mock for a posts
API endpoint, use
ember g http-mock posts
A basic ExpressJS server will be scaffolded for
your endpoint under /your-app/server/mocks/posts.js
. Once you add the
appropriate JSON response, you’re ready to go. The next time you run
ember server
, your new mock server will be listening for any API requests
from your Ember app.
Note: Mocks are just for development. The entire
/server
directory will be ignored duringember build
andember test
.
If you decide to use fixtures instead of mocks, you’ll need to use
reopenClass
within your model class definitions. First, create a fixture
adapter, either for a single model or your entire application:
// adapters/application.js
import DS from 'ember-data';
const { FixtureAdapter } = DS;
export default FixtureAdapter.extend({});
Then add fixture data to your model class:
// models/author.js
import DS from 'ember-data';
const { Model, attr } = DS;
let Author = Model.extend({
firstName: attr('string'),
lastName: attr('string')
});
Author.reopenClass({
FIXTURES: [
{id: 1, firstName: 'Bugs', lastName: 'Bunny'},
{id: 2, firstName: 'Wile E.', lastName: 'Coyote'}
]
});
export default Author;
Your Ember app’s API requests will now use your fixture data.
Testing
Running existing tests
Running your tests is as easy as one of these commands:
ember test # will run your test-suite in your current shell once
ember test --server # will run your tests on every file-change
ember t -s -m 'Unit | Utility | name' # will run only the unit test module for a utility by `name`, on every change
ember t -f 'match a phrase in test description(s)' # will run only the tests with a description that matches a phrase
Alternatively you can run the tests in your regular browser using the QUnit
interface. Run ember server
and navigate to http://localhost:4200/tests
.
When the app runs in /tests it runs in the development environment, not the
test environment.
Writing a test
- ember-testing
- helpers
- unit/acceptance
The default tests in Ember CLI use the QUnit library, though Mocha / Chai testing is possible using ember-cli-mocha. The included tests demonstrate how to write both unit tests and acceptance/integration tests using the new ember-testing package.
Test filenames should be suffixed with -test.js
in order to run.
If you have manually set the locationType in your environment.js to hash
or
none
you need to update your tests/index.html
to have absolute paths
(/assets/vendor.css
and /testem.js
vs the default relative paths).
CI Mode with Testem
ember test
will run your tests with Testem
on CI mode. You can
pass any option to Testem
using a configuration file.
If you are capturing output from the Testem
xunit reporter, use ember test
--silent
to silence unwanted output such as the ember-cli version. If you want
to capture output to a file you can use report_file: "path/to/file.xml"
in
your testem.json
config file.
By default, your integration tests will run on PhantomJS. You can install via npm:
npm install -g phantomjs-prebuilt
We plan to make your test runner pluggable, so you can use your favorite runner.
Using ember-qunit for integration tests
All Ember Apps come with built-in ember test
helpers,
which are useful for writing integration tests.
In order to use them, you will need to import tests/helpers/start-app.js
,
which injects the required helpers.
Be sure to use the module
function to invoke beforeEach
and afterEach
.
import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import startApp from '../helpers/start-app';
let App;
module('An Integration test', {
beforeEach() {
App = startApp();
},
afterEach() {
run(App, App.destroy);
}
});
test("Page contents", function(assert) {
assert.expect(2);
visit('/foos').then(function() {
assert.equal(find('.foos-list').length, 1, "Page contains list of models");
assert.equal(find('.foos-list .foo-item').length, 5, "List contains expected number of models");
});
});
Using ember-qunit for unit tests
An Ember CLI-generated project comes pre-loaded with ember-qunit which includes several helpers to make your unit-testing life easier, i.e.:
- moduleFor
- moduleForModel
- moduleForComponent
moduleFor(fullName, description, callbacks, delegate)
The generic resolver that will load what you specify. The usage closely follows
QUnit’s own module
function. Its use can be seen within the supplied
index-test.js:
// tests/unit/routes/index-test.js
import { test, moduleFor } from 'ember-qunit';
moduleFor('route:index', "Unit - IndexRoute", {
// only necessary if you want to load other items into the runtime
// needs: ['controller:index']
beforeEach() { },
afterEach() { }
});
test("it exists", function(assert){
assert.ok(this.subject());
});
fullname
The resolver friendly name of the object you are testing.
description
The description that will group all subsequent tests under. Defaults to the fullname
.
callbacks
You are able to supply custom beforeEach, afterEach, & subject functionality by
passing them into the callbacks parameter. If other objects should be loaded
into Ember.js, specify the objects through the needs
property.
delegate
To manually modify the container & the testing context, supply a function as the delegate
matching this signature delegate(container, testing_context)
.
this.subject()
calls the factory for the object specified by the fullname and
will return an instance of the object.
moduleForModel(name, description, callbacks)
Extends the generic moduleFor
with custom loading for testing models:
// tests/unit/models/post-test.js
import DS from 'ember-data';
import { run } from '@ember/runloop';
import { get, set } from '@ember/object';
import { test, moduleForModel } from 'ember-qunit';
const { Model } = DS;
moduleForModel('post', 'Post Model', {
needs: ['model:comment']
});
test('Post is a valid ember-data Model', function (assert) {
let store = this.store();
let post = this.subject({ title: 'A title for a post', user: 'bob' });
assert.ok(post);
assert.ok(post instanceof Model);
// set a relationship
run(() => {
set(post, 'comment', store.createRecord('comment', { }))
});
assert.ok(get(post, 'comment'));
assert.ok(get(post, 'comment') instanceof Model);
});
name
The name of the model you are testing. It is necessary to only supply the name,
not the resolver path to the object(model:post
=> post
).
description
The description that will group all subsequent tests under. Defaults to the name
.
callbacks
You are able to supply custom beforeEach, afterEach, & subject functionality by
passing them into the callbacks parameter. If other objects should be loaded
into Ember.js, specify the objects through the needs
property.
Note: If the model you are testing has relationships to any other model, those must
be specified through the needs
property.
this.store()
retrieves the DS.Store
.
this.subject()
calls the factory for the DS.Model
specified by the fullname and
will return an instance of the object.
moduleForComponent(name, description, callbacks)
Extends the generic moduleFor
with custom loading for testing components:
// tests/integration/components/pretty-color-test.js
import { run } from '@ember/runloop';
import { set } from '@ember/object';
import { test, moduleForComponent } from 'ember-qunit';
moduleForComponent('pretty-color');
test('changing colors', function(assert){
let component = this.subject();
run(() => {
set(component, 'name','red');
});
// first call to $() renders the component.
assert.equal(this.$().attr('style'), 'color: red;');
run(() => {
set(component, 'name', 'green');
});
assert.equal(this.$().attr('style'), 'color: green;');
});
name
The name of the component you are testing. It is necessary to only supply the name,
not the resolver path to the object(component:pretty-color
=> pretty-color
).
description
The description that will group all subsequent tests under. Defaults to the name
.
callbacks
You are able to supply custom beforeEach, afterEach, & subject functionality by
passing them into the callbacks parameter. If other objects should be loaded
into Ember.js, specify the objects through the needs
property.
this.subject()
calls the factory for the import Component from '@ember/runloop';
specified by the
fullname and will return an instance of the object.
The first call this.$()
will render out the component. So if you want to test
styling, you must access the component via jQuery.
Writing your own test helpers
Ember testing provides that ability to register your own test helpers. In order
to use these with ember-cli they must be registered before startApp
is
defined.
Depending on your approach, you may want to define one helper per file or a group of helpers in one file.
Single helper per file
// helpers/routes-to.js
import { registerAsyncHelper } from '@ember/test';
export default registerAsyncHelper('routesTo', function (app, assert, url, route_name) {
visit(url);
andThen(() => {
assert.equal(currentRouteName(), route_name, `Expected ${route_name}, got: ${currentRouteName()}`);
});
});
This can then be used in start-app.js
, like this
// helpers/start-app.js
import routesTo from './routes-to';
export default function startApp(attrs) {
//...
Group of helpers in one file
An alternative approach is to create a bunch of helpers wrapped in a self calling function, like this
// helpers/custom-helpers.js
import { registerAsyncHelper, registerHelper } from '@ember/test';
const customHelpers = function() {
registerHelper('myGreatHelper', function (app) {
//do awesome test stuff
});
registerAsyncHelper('myGreatAsyncHelper', function (app) {
//do awesome test stuff
});
}();
export default customHelpers;
which can be used in start-app.js
// helpers/start-app.js
import customHelpers from './custom-helpers';
export default function startApp(attrs) {
//...
Once your helpers are defined, you’ll want to ensure that they are listed in the .eslintrc.js
file within the test directory.
// /tests/.eslintrc.js
{
"globals": {
"myGreatHelper": true,
"myGreatAsyncHelper": true
Asset Compilation
Raw Assets
public/assets
vsapp/styles
To add images, fonts, or other assets, place them in the public/assets
directory. For
example, if you place logo.png
in public/assets/images
, you can reference it in
templates with assets/images/logo.png
or in stylesheets with
url('/assets/images/logo.png')
.
This functionality of Ember CLI comes from broccoli-asset-rev. Be sure to check out all the options and usage notes.
JS Transpiling
Ember CLI automatically transpiles future JavaScript (ES6/ES2015, ES2016 and beyond) into standard ES5 JavaScript that runs on every browser using Babel JS with the Ember CLI Babel addon.
Internally, Ember CLI Babel uses babel-preset-env
, which figures out which parts of your code
need to be transpiled to ES5 by analyzing your project’s browser support targets. A target
is a special keyword
that maps to a browserlist support rule. These are defined in your
config/targets.js
file, which Ember CLI generates like so:
/* eslint-env node */
module.exports = {
browsers: [
'ie 9',
'last 1 Chrome versions',
'last 1 Firefox versions',
'last 1 Safari versions'
]
};
(If these values look familiar, they’re the same exact values used by the popular Autoprefixer project.)
If you need more fine-grained customization over the way that babel-preset-env
transforms your code,
simply set any of the options found here on your application’s babel
hash in ember-cli-build.js
.
For example, if you wanted to explicitly exclude generator function transpilation from your output, your configuration would look like this:
// ember-cli-build.js
/* eslint-env node */
'use strict';
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
babel: {
blacklist: [
'es6.arrowFunctions',
'es6.blockScoping',
'es6.classes',
'es6.destructuring',
'es6.parameters',
'es6.properties.computed',
// ...more options
]
}
});
//...
return app.toTree();
};
As of Version 2.13, Ember CLI uses Babel 6.X for transpilation. Ember CLI versions prior to 2.13 use Babel Babel 5.X, and you can check its documentation for a comprehensive list of all available transformations and options.
Minifying
The compiled css-files are minified by broccoli-clean-css
or broccoli-csso
,
if it is installed locally. You can pass minifer-specific options to them using
the minifyCSS:options
object in your ember-cli-build. Minification is enabled by
default in the production-env and can be disabled using the minifyCSS:enabled
switch.
Similarly, the js-files are minified with broccoli-uglify-js
in the
production-env by default. You can pass custom options to the minifier via the
minifyJS:options
object in your ember-cli-build. To enable or disable JS minification
you may supply a boolean value for minifyJS:enabled
.
For example, to disable minifying of CSS and JS, add in ember-cli-build.js
:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
minifyJS: {
enabled: false
},
minifyCSS: {
enabled: false
}
});
//...
return app.toTree();
};
Exclude from minification
To exclude assets from dist/assets
from being minificated, one can pass options for
broccoli-uglify-sourcemap like so:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
minifyJS: {
options: {
exclude: ["**/vendor.js"]
}
}
});
//...
return app.toTree();
};
This would exclude the resulting vendor.js
file from being minificated.
Source Maps
Ember CLI supports producing source maps for your concatenated and minified JS source files.
Source maps are configured by the EmberApp sourcemaps
option, and
are disabled in production by default. Pass sourcemaps: {enabled: true}
to your EmberApp constructor to enable source maps for javascript. Use the extensions
option to add other formats, such as coffeescript and CSS: {extensions: ['js', 'css', 'coffee']}
. JS is supported out-of-the-box. CSS is not currently supported. For other source formats (Sass, Coffee, etc) refer to their addons.
Default ember-cli-build.js:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
sourcemaps: {
enabled: EmberApp.env() !== 'production',
extensions: ['js']
}
});
//...
return app.toTree();
};
Stylesheets
Ember CLI supports plain CSS out of the box. You can add your css styles to
app/styles/app.css
and it will be served at assets/application-name.css
.
For example, to add bootstrap in your project you need to do the following:
bower install bootstrap --save
In ember-cli-build.js
add the following:
app.import('bower_components/bootstrap/dist/css/bootstrap.css');
it’s going to tell Broccoli that we want
this file to be concatenated with our vendor.css
file.
To use a CSS preprocessor, you’ll need to install the appropriate
Broccoli plugin. When using a
preprocessor, Broccoli is configured to look for an app.less
, app.scss
, app.sass
,
or app.styl
manifest file in app/styles
. This manifest should import any
additional stylesheets.
All your preprocessed stylesheets will be compiled into one file and served at
assets/application-name.css
.
If you would like to change this behavior, or compile to multiple output stylesheets, you can adjust the Output Paths Configuration
CSS
To use plain CSS with app.css
:
- Write your styles in
app.css
and/or organize your CSS into multiple stylesheet files and import these files with@import
from withinapp.css
. - CSS
@import
statements (e.g.@import 'typography.css';
) must be valid CSS, meaning@import
statements must precede all other rules and so be placed at the top ofapp.css
.
To process your imports and replace them with the contents of their files,
add in ember-cli-build.js
:
// ember-cli-build.js
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
minifyCSS: {
options: { processImport: true }
}
});
//...
return app.toTree();
};
Which will cause the following to happen:
- In the production build, the
@import
statements are replaced with the contents of their files and the final minified, concatenated single CSS file is built todist/assets/yourappname-FINGERPRINT_GOES_HERE.css
. - Any individual CSS files are also built and minified into
dist/assets/
in case you need them as standalone stylesheets. - Relative pathing gets changed (how to customize?)
Example app.css
with valid @import
usage:
/* @imports must appear at top of stylesheet to be valid CSS */
@import 'typography.css';
@import 'forms.css';
/* Any CSS rules must go *after* any @imports */
.first-css-rule {
color: red;
}
...
CSS Preprocessors
To use one of the following preprocessors, all you need to do is install the appropriate NPM module. The respective files will be picked up and processed automatically.
LESS
To enable LESS, you’ll need to add ember-cli-less to your NPM modules.
ember install ember-cli-less
SCSS/SASS
To enable SCSS/SASS, you’ll need to install the ember-cli-sass addon to your project (defaults to .scss, .sass allowed via configuration).
ember install ember-cli-sass
You can configure your project to use .sass in your ember-cli-build.js
:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
sassOptions: {
extension: 'sass'
}
});
//...
return app.toTree();
};
Compass
To use Compass with your ember-cli app, install ember-cli-compass-compiler addon using NPM.
ember install ember-cli-compass-compiler
Stylus
To enable Stylus, you must first add ember-cli-stylus to your NPM modules:
ember install ember-cli-stylus
CoffeeScript
To enable CoffeeScript, you must first add ember-cli-coffeescript to your NPM modules:
ember install ember-cli-coffeescript
The modified package.json
should be checked into source control. CoffeeScript
can be used in your app’s source and in tests, just use the .coffee
extension
on any file. We recommend using version >= 1.16.0 of ember-cli-coffeescript to avoid the need
to escape import
and export
statements.
Note that earlier versions of the transpiler had explicit support for CoffeeScript, but that support has been removed.
EmberScript
To enable EmberScript, you must first add broccoli-ember-script to your NPM modules:
npm install broccoli-ember-script --save-dev
Note that the ES6 module transpiler is not directly supported with Emberscript,
to allow use of ES6 modules use the `
character to escape raw Javascript
similar to the CoffeeScript example above.
Emblem
For Emblem, run the following commands:
ember install ember-cli-emblem
If you’re using the older broccoli-emblem-compiler addon, you need to switch to ember-cli-emblem. The older broccoli-emblem-compiler compiles directly to JS instead of Handlebars and therefore is broken on all newer version of HTMLBars.
Fingerprinting and CDN URLs
Fingerprinting is done using the addon broccoli-asset-rev (which is included by default).
When the environment is production (e.g. ember build --environment=production
),
the addon will automatically fingerprint your js, css, png, jpg, and gif assets
by appending an md5 checksum to the end of their filename
(e.g. assets/yourapp-9c2cbd818d09a4a742406c6cb8219b3b.js
). In addition, your
html, js, and css files will be re-written to include the new name. There are
a few options you can pass in to EmberApp
in your ember-cli-build.js
to customize
this behavior.
enabled
- Default:app.env === 'production'
- Boolean. Enables fingerprinting if true. True by default if current environment is production.exclude
- Default:[]
- An array of strings. If a filename contains any item in the exclude array, it will not be fingerprinted.ignore
- Default:[]
- An array of strings. If a filename contains any item in the ignore array, the contents of the file will not be processed for fingerprinting.extensions
- Default:['js', 'css', 'png', 'jpg', 'gif', 'map']
- The file types to add md5 checksums.prepend
- Default:''
- A string to prepend to all of the assets. Useful for CDN urls likehttps://subdomain.cloudfront.net/
replaceExtensions
- Default:['html', 'css', 'js']
- The file types to replace source code with new checksum file names.customHash
- When specified, this is appended to fingerprinted filenames instead of the md5. Passnull
to suppress the hash, which can be useful when usingprepend
.
As an example, this ember-cli-build
will exclude any file in the fonts/169929
directory as well as add a cloudfront domain to each fingerprinted asset.
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
fingerprint: {
exclude: ['fonts/169929'],
prepend: 'https://subdomain.cloudfront.net/'
}
});
//...
return app.toTree();
};
The end result will turn
<script src="assets/appname.js">
background: url('/images/foo.png');
into
<script src="https://subdomain.cloudfront.net/assets/appname-342b0f87ea609e6d349c7925d86bd597.js">
background: url('https://subdomain.cloudfront.net/images/foo-735d6c098496507e26bb40ecc8c1394d.png');
You can disable fingerprinting in your ember-cli-build.js
:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
fingerprint: {
enabled: false
}
});
//...
return app.toTree();
};
Or remove the entry from your EmberApp
and broccoli-asset-rev
from your package.json
.
Application Configuration
Application configurations from your ember-cli-build.js
file will be stored inside a
special meta tag in dist/index.html
.
sample meta tag:
<meta name="user/config/environment" content="%7B%22modulePre.your.config">
This meta tag is required for your ember application to function properly.
If you prefer to have this tag be part of your compiled javascript files
instead, you may use the storeConfigInMeta
flag in ember-cli-build.js
.
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
storeConfigInMeta: false
});
//...
return app.toTree();
};
Configuring output paths
The compiled files are output to the following paths:
Assets | Output File |
---|---|
app/index.html |
/index.html |
app/*.js |
/assets/application-name.js |
app/styles/app.css |
/assets/application-name.css |
other CSS files in app/styles |
same filename in /assets |
JavaScript files you import with app.import() |
/assets/vendor.js |
CSS files you import with app.import() |
/assets/vendor.css |
To change these paths, specify the outputPaths
config option in ember-cli-build.js
. The default setting is shown here:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
outputPaths: {
app: {
html: 'index.html',
css: {
'app': '/assets/application-name.css'
},
js: '/assets/application-name.js'
},
vendor: {
css: '/assets/vendor.css',
js: '/assets/vendor.js'
}
}
});
//...
return app.toTree();
};
You may edit any of these output paths, but make sure to update the paths specified in your
app.outputPaths.app.html
default it is index.html
, and tests/index.html
. If this is not done,
your app will not be served with correct asset names.
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
outputPaths: {
app: {
js: '/assets/main.js'
}
}
});
//...
return app.toTree();
};
The outputPaths.app.css
option uses a key value relationship. The key is
the input file and the value is the output location. Note that we do not
include the extension for the input path, because each preprocessor has a
different extension.
When using CSS preprocessing, only the app/styles/app.scss
(or .less
etc)
is compiled. If you need to process multiple files, you must add another key:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
outputPaths: {
app: {
css: {
'app': '/assets/application-name.css',
'themes/alpha': '/assets/themes/alpha.css'
}
}
}
});
//...
return app.toTree();
};
Integration
When using Ember inside another project, you may want to launch Ember only when
a specific route is accessed. If you’re preloading the Ember javascript before
you access the route, you have to disable autoRun
:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp({
autoRun: false
});
//...
return app.toTree();
};
To manually run Ember:
require("app-name/app")["default"].create({/* app settings */});
Subresource integrity
SRI calculation is done using the addon ember-cli-sri (which is included by default).
This plugin is used to generate SRI integrity for your applications. Subresource integrity is a security concept used to check JavaScript and stylesheets are loaded with the correct content when using a CDN.
Why
The reason to add this to your application is to protect against poisoned CDNs breaking JavaScript or CSS.
- JavaScript DDoS prevention
- The latest GitHub DDoS attack
- Protection against corrupted code on less trusted servers
Customize
To customize SRI generation see: ember-cli-sri
Managing Dependencies
NPM/Yarn and Bower Configuration
Ember CLI supports NPM, yarn
and optionally Bower for dependency management. It will
detect whether you are using npm or yarn by the presence of a yarn.lock
file
in your project.
A newly generated Ember CLI project only has NPM dependencies, so you will
notice a package.json
file at the root of your project, but not a bower.json
.
To use Bower packages, you will have to first run bower init
to create a
bower.json
file also at the root of the project.
NPM’s package.json
together with Bower’s bower.json
allow you to declare
the dependencies of your project.
Changes to your dependencies should be managed through these files, rather
than manually installing packages individually.
Executing npm install
will install all of the dependencies listed in
package.json
in one step. Similarly, executing bower install
will install
all of the dependencies listed in bower.json
in one step.
Ember CLI is configured to have git ignore your bower_components
and
node_modules
directories by default. Using the Bower and NPM configuration
files allows collaborators to fork your repo and get their dependencies installed
locally by executing npm install
and bower install
themselves.
Ember CLI watches bower.json
for changes. Thus it reloads your app if you
install new dependencies via bower install <dependencies> --save
. If you
install NPM dependencies via npm install <dependencies> --save
, you will need
to restart your Ember CLI server session manually.
Further documentation about NPM and Bower is available at their official documentation pages:
Note that it is often easiest to install Ember addon dependencies using the
ember install
command, which will save all dependencies to the correct
configuration files and run any further setup steps required.
Compiling Assets
Ember CLI uses the Broccoli assets pipeline.
The assets manifest is located in the ember-cli-build.js
file in your project
root (not the default ember-cli-build.js
).
To add an asset specify the dependency in yourember-cli-build.js
before
calling app.toTree()
. You can only import assets that are within the
bower_components
or vendor
directories. The following example scenarios illustrate how this works.
Javascript Assets
Standard Non-AMD Asset
First, provide the asset path as the first and only argument:
app.import('bower_components/moment/moment.js');
From here you would use the package as specified by its documentation, usually a global variable. In this case it would be:
import Ember from 'ember';
/* global moment */
// No import for moment, it's a global called `moment`
// ...
let day = moment('Dec 25, 1995');
Note: Don’t forget to make ESLint happy by adding a /* global MY_GLOBAL */
to your module, or by defining it within the globals
section of your
.eslintrc.js
file.
Alternatively, you could generate an ES6 shim to make the library accessible
via import
.
First, generate the shim:
ember generate vendor-shim moment
Next, provide the vendor asset path:
app.import('vendor/shims/moment.js');
Finally, use the package by adding the appropriate import
statement:
import moment from 'moment';
// ...
let day = moment('Dec 25, 1995');
Standard Named AMD Asset
Provide the asset path as the first argument, and the list of modules and exports as the second:
app.import('bower_components/ic-ajax/dist/named-amd/main.js');
To use this asset in your app, import it.
For example, with ic-ajax
, when to use ic.ajax.raw
:
import { raw as icAjaxRaw } from 'ic-ajax';
//...
icAjaxRaw( /* ... */ );
Standard Anonymous AMD Asset
Provide the asset path as the first argument, and the desired module name in the second:
app.import('bower_components/ic-ajax/dist/amd/main.js', {
using: [
{ transformation: 'amd', as: 'ic-ajax' }
]
});
To use this asset in your app, import it.
For example, with ic-ajax
, when to use ic.ajax.raw
:
import { raw as icAjaxRaw } from 'ic-ajax';
//...
icAjaxRaw( /* ... */ );
Environment Specific Assets
If you need to use different assets in different environments, specify an object as the first parameter. That object’s key should be the environment name, and the value should be the asset to use in that environment.
app.import({
development: 'bower_components/ember/ember.js',
production: 'bower_components/ember/ember.prod.js'
});
If you need to import an asset in one environment but not import it or any
alternatives in other environments then you can wrap app.import
in an if
statement.
if (app.env === 'development') {
app.import('vendor/ember-renderspeed/ember-renderspeed.js');
}
Customizing a built-in Asset
This is somewhat non-standard and discouraged, but suppose that due to a
requirement in your application that you need to use the full version of
Handlebars even in the production environment. You would simply provide the
path to the EmberApp
constructor:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
vendorFiles: {
'handlebars.js': {
production: 'bower_components/handlebars/handlebars.js'
}
}
});
//...
return app.toTree();
};
Alternatively, if you want to exclude the built-in asset from being
automatically included in vendor.js
, you can set its value to false
:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
vendorFiles: {
'handlebars.js': false
}
});
//...
return app.toTree();
};
Note: The built-in assets are required dependencies needed by the environment to run your app. If you use the above method to specifically exclude some, you should still be including them in some other way.
Whitelisting and Blacklisting Assets
You can limit which dependencies in your package.json file get imported into
your Ember application by using the addons option of the EmberApp constructor. A
whitelist
parameter allows you to restrict modules to a specific list. A
blacklist
parameter excludes specific modules from being imported into your
app:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
addons: {
blacklist: [
'fastboot-app-server'
]
}
});
//...
return app.toTree();
};
Test Assets
You may have additional libraries that should only be included when running tests (such as qunit-bdd or sinon). These can be imported into your app in your ember-cli-build.js:
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const isProduction = EmberApp.env() === 'production';
module.exports = function(defaults) {
let app = new EmberApp(defaults, { });
if ( !isProduction ) {
app.import( app.bowerDirectory + '/sinonjs/sinon.js', { type: 'test' } );
app.import( app.bowerDirectory + '/sinon-qunit/lib/sinon-qunit.js', { type: 'test' } );
}
return app.toTree();
};
Notes:
- Be sure to pass
{ type: 'test' }
as the second argument toapp.import
. This will ensure that your libraries are compiled into thetest-support.js
file.
Styles
Static CSS
Provide the asset path as the first argument:
app.import('bower_components/foundation/css/foundation.css');
All style assets added this way will be concatenated and output as
/assets/vendor.css
.
Dynamic Styles (SCSS, LESS, etc)
The vendor trees that are provided upon instantiation are available to your
dynamic style files. Take the following example (in app/styles/app.scss
):
@import "bower_components/foundation/scss/normalize.scss";
Other Assets
Using app.import()
All other assets like images or fonts can also be added via import()
. By default, they
will be copied to dist/
as they are.
app.import('bower_components/font-awesome/fonts/fontawesome-webfont.ttf');
This example would create the font file in dist/font-awesome/fonts/fontawesome-webfont.ttf
.
You can also optionally tell import()
to place the file at a different path.
The following example will copy the file to dist/assets/fontawesome-webfont.ttf
.
app.import('bower_components/font-awesome/fonts/fontawesome-webfont.ttf', {
destDir: 'assets'
});
If you need to load certain dependencies before others, you can set the
prepend
property equal to true
on the second argument of import()
. This
will prepend the dependency to the vendor file instead of appending it, which
is the default behavior.
app.import('bower_components/es5-shim/es5-shim.js', {
type: 'vendor',
prepend: true
});
If you need some of your assets to be included into specific file you can
provide an outputFile
option for your import:
// ember-cli-build.js
app.import('vendor/dependency-1.js', { outputFile: 'assets/additional-script.js'});
app.import('vendor/dependency-2.js', { outputFile: 'assets/additional-script.js'});
As a result both dependencies will end up in dist/assets/additional-script.js
in the same order they were specified.
Note: outputFile
works only for javascript and css files.
Using broccoli-funnel
With the broccoli-funnel package, (parts of) a bower-installed package can be used as assets as-is. First ensure that the Broccoli package needed to build is installed:
npm install broccoli-funnel --save-dev
Add this import to the top of ember-cli-build.js
, just below the EmberApp
require:
const Funnel = require('broccoli-funnel');
Within ember-cli-build.js
, we merge assets from a bower dependency with the main app tree:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');
module.exports = function(defaults) {
let app = new EmberApp(defaults, { });
// Copy only the relevant files. For example the WOFF-files and stylesheets for a webfont:
let extraAssets = new Funnel('bower_components/a-lovely-webfont', {
srcDir: '/',
include: ['**/*.woff', '**/stylesheet.css'],
destDir: '/assets/fonts'
});
// Providing additional trees to the `toTree` method will result in those
// trees being merged in the final output.
return app.toTree(extraAssets);
};
In the above example the assets from the fictive bower dependency called a-lovely-webfont
can now
be found under /assets/fonts/
, and might be linked to from index.html
like so:
<link rel="stylesheet" href="assets/fonts/lovelyfont_bold/stylesheet.css">
You can exclude assets from the final output in a similar fashion. For example,
to exclude all .gitkeep
files from the final output:
// Again, add this import to the top of `ember-cli-build.js`, just below the `EmberApp` require:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');
module.exports = function(defaults) {
let app = new EmberApp(defaults, { });
// Filter toTree()'s output
let filteredAssets = new Funnel(app.toTree(), {
// Exclude gitkeeps from output
exclude: ['**/.gitkeep']
});
// Export filtered tree
return filteredAssets;
};
Note: broccoli-static-compiler is deprecated. Use broccoli-funnel instead.
Environments
Ember CLI ships with support for managing your application’s environment.
Ember CLI will build an environment config file at config/environment
. Here,
you can define an ENV object for each environment (development, test and
production). For now, this is limited to the three environments mentioned.
The ENV object contains two important keys: 1) EmberENV, and 2) APP. The first can be used to define Ember feature flags (see the Feature Flags guide). The second can be used to pass flags/options to your application instance.
You can access these environment variables in your application code by
importing from ../config/environment
or
your-application-name/config/environment
.
For example:
import ENV from 'your-application-name/config/environment';
if (ENV.environment === 'development') {
// ...
}
You can also import the ENV object in ./ember-cli-build.js
in case you need environment variables in the build script.
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const ENV = require('./config/environment')(EmberApp.env());
Ember CLI assigns ENV.EmberENV
to window.EmberENV
, which Ember reads on application initialization.
Additionally, Ember CLI contains a number of environment-dependent helpers for assets:
It is now also possible to override command line options by creating a file in
your app’s root directory called .ember-cli
and placing desired overrides in
it.
For example, a common desire is to change the
port
that ember-cli serves the app from. It’s possible to pass the port number
directly to ember server in the command line, e.g. ember server --port 8080
.
If you wish to make this change a permanent configuration change, make the
.ember-cli
file and add the options you wish to pass to the server in a hash.
{
"port": 8080
}
Deployments
You can easily deploy your Ember CLI application to a number of places using ember-cli-deploy.
Additional Examples:
- https://www.heroku.com/emberjs
History API and Root URL
If you are deploying the app to somewhere other than the rootURL
(/
),
you will need to configure the value of rootURL
in config/environment.js
.
This is required for the History API, and thus also the Router, to function correctly.
For example
// config/environment.js
if (environment === 'production') {
ENV.rootURL = '/path/to/ember/app/';
}
This will also be used as a prefix for assets, eg /path/to/ember/app/assets/vendor.js
. However when
building for production the value of prepend
for fingerprint
will be used instead. So for
ember build --prod
with
// ember-cli-build.js
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
// Add options here
fingerprint: {
prepend: 'https://cdn.example.com/'
}
});
return app.toTree();
};
the asset URLs will not use rootURL
and will instead be:
https://cdn.example.com/assets/vendor-3b1b39893d8e34a6d0bd44095afcd5c4.js
.
As of version 2.7, baseURL
is deprecated and rootURL
should be used
instead. See this blog post
for more details.
Content Security Policy
For those interested in enhanced security for their web application, they should consider the setting up a content-security policy even for development. That way security violations can be discovered immediately, rather than in production.
For more information, see the ember-cli-content-security-policy
README.
Deploying an HTTPS server using Nginx on a Unix/Linux/MacOSx machine
The following is a simple deployment with https using nginx. Http just redirects to the https server here. Don’t forget to include your ssl keys in your config.
Before deployment make sure you run this command to populate the dist directory:
ember build --environment="production"
File: nginx.conf
## Nginx Production Https Ember Server Configuration
## https site##
server {
listen 443 default;
server_name <your-server-name>;
#root /usr/share/nginx/html;
root <root path to an ember /dist directory>;
index index.html index.htm;
# log files
access_log /var/log/nginx/<your-server-name>.access.log;
error_log /var/log/nginx/<your-server-name>.error.log;
# ssl files
ssl on;
keepalive_timeout 60;
# include information on SSL keys, cert, protocols and ciphers
# SSLLabs.com is a great resource for this, along with testing
# your SSL configuration: https://www.ssllabs.com/projects/documentation/
# Strict Transport Security
add_header Strict-Transport-Security max-age=2592000;
# proxy buffers
proxy_buffers 16 64k;
proxy_buffer_size 128k;
## default location ##
location / {
include /etc/nginx/mime.types;
try_files $uri $uri/ /index.html?/$request_uri;
}
}
## http redirects to https ##
server {
listen 80;
server_name <your-server-name>;
rewrite ^/.*$ https://$host$request_uri? permanent;
}
Upgrading
Upgrading an Ember CLI App
To upgrade an Ember CLI app, first delete temporary folders by running rm -rf node_modules dist tmp
.
Then, if you are using yarn, run:
yarn upgrade ember-cli
If you are using npm, run:
npm install --save-dev ember-cli
After upgrading your project’s dependencies, run ember init
to apply the blueprint changes.
Please follow the prompts, and review all changes. The most common source of upgrade pain is missing changes in this step, you can press d
to see a diff when reviewing.
Steps to upgrade to the latest version of Ember CLI are also included with the release notes for each release.
Common Issues
npm
Package Management with sudo
Installing packages such as bower
with sudo
powers can lead to permissions
issues and ultimately to problems installing dependencies. See
https://gist.github.com/isaacs/579814
for a collection of various solutions.
Installing From Behind a Proxy
If you’re behind a proxy, you might not be able to install because Ember CLI–or
some of its dependencies–tries to git clone
a git://
URL. (In this scenario,
only http://
URLs will work).
You’ll probably get an error like this:
npm ERR! git clone git://github.com/jgable/esprima.git Cloning into bare repository '/home/<username>/.npm/_git-remotes/git-github-com-jgable-esprima-git-d221af32'...
npm ERR! git clone git://github.com/jgable/esprima.git
npm ERR! git clone git://github.com/jgable/esprima.git fatal: unable to connect to github.com:
npm ERR! git clone git://github.com/jgable/esprima.git github.com[0: 192.30.252.129]: errno=Connection timed out
npm ERR! Error: Command failed: fatal: unable to connect to github.com:
npm ERR! github.com[0: 192.30.252.129]: errno=Connection timed out
As a workaround you can configure git
to make the translation:
git config --global url."https://".insteadOf git://
Using Canary Build instead of release
For Ember: bower install ember#canary --resolution canary
For ember-data
: npm install --save-dev emberjs/data#master
Windows Build Performance Issues
See The Windows Section for more details.
PhantomJS on Windows
When running tests on Windows via PhantomJS the following error can occur:
events.js:72
throw er; // Unhandled 'error' event
^
Error: spawn ENOENT
at errnoException (child_process.js:988:11)
at Process.ChildProcess._handle.onexit (child_process.js:779:34)
In order to fix this ensure the following is added to your PATH
:
C:\Users\USER_NAME\AppData\Roaming\npm\node_modules\phantomjs\lib\phantom
Cygwin on Windows
Node.js on Cygwin is no longer supported more details Rather then using Cygwin, we recommend running Ember CLI natively on windows, or via the new Windows Subsystem Linux.
Usage with Docker
When building your own Docker image to build Ember applications and run tests, there are a couple of pitfalls to avoid.
- PhantomJS requires
bzip2
andfontconfig
to already be installed. - After installing PhantomJS, you will need to manually link PhantomJS to
/usr/local/bin
if that is not done by the install process. - Testem uses the
which
command to locate PhantomJS, so you must installwhich
if it is not included in your base OS.
Usage with Vagrant
Vagrant is a system for automatically creating and setting up development environments that run in a virtual machine (VM).
Running your Ember CLI development environment from inside of a Vagrant VM will require some additional configuration and will carry a few caveats.
Ports
In order to access your Ember CLI application from your desktop’s web browser, you’ll have to open some forwarded ports into your VM. Ember CLI by default uses two ports.
- For serving assets the default is
4200
. Can be configured via--port 4200
. - For live reload there is no default. Can be configured via
---live-reload-port=9999
.
To make Vagrant development seamless these ports will need to be forwarded.
Vagrant.configure("2") do |config|
# ...
config.vm.network "forwarded_port", guest: 4200, host: 4200
config.vm.network "forwarded_port", guest: 9999, host: 9999
end
Watched Files
The way Vagrant syncs directories between your desktop and vm may prevent file watching from working correctly. This will prevent rebuilds and live reloads from working correctly. There are several work arounds:
- Watch for changes by polling the file system via:
ember serve --watcher polling
. - Use nfs for synced folders.
VM Setup
When setting up your VM, install Ember CLI dependencies as you normally would. Some of these dependencies (such as broccoli-sass) may have native depenencies that may require recompilation. To do so run:
npm rebuild
Provider
The two most common Vagrant providers, VirtualBox and VMware Fusion, will both work. However, VMware Fusion is substantially faster and will use less battery life if you’re on a laptop. As of now, VirtualBox will use 100% of a single CPU core to poll for file system changes inside of the VM.
Windows
Windows
Windows Vista and newer windows versions are fully supported.
To get started ensure the following dependencies are installed:
- Node.js - https://nodejs.org/en/
- Git - https://git-scm.com/
- Phantom.js - http://phantomjs.org/
Performance
Although supported, Windows performance, at least by default, isn’t as good as on Linux or MacOS. On a positive note, this story continues to improve. Both Microsoft, and the Ember CLI team continue to work to improve these developer ergonomics.
What causes the build slowdown?
The two primary reasons are:
- Lack of enabled-by-default symlinks
- Generally slower FS operations on NTFS
For the best possible Windows experience
- Windows 10, insiders release with development mode enabled. Symlinks are enabled by default) Details from Microsoft
- or, Windows Subsystem Linux Installation Guide
Improving your Windows experience
Ensure Search and Defender ignore your project’s tmp
directory:
npm install --save-dev ember-cli-windows-addon
Then, to start the automatic configuration, run:
ember windows
Read more about this from the Microsoft DX Open Source team
Enabling symlinks
To create symlinks the account running Ember CLI must have the
SeCreateSymbolicLinkPrivilege
. Users in the Administrators group have this
permission already. However, if UAC (User Access Control) is enabled, users in
the Administrators group must run their shell using Run As Administrator
because UAC strips away certain permissions from the Administrators +group,
including SeCreateSymbolicLinkPrivilege
.
If the user account is not part of the Administrators group you will need to
add the SeCreateSymbolicLinkPrivilege
to allow the creation of symlinks. To
do this open the Local Security Policy
by typing Local Security Policy in the
Windows Run
Box.
Under Local Policies
-> User Rights Assignment
find the Create symbolic
links
policy and double click it to add a new user or group. Once your user or
group has been added, your user should be able to create symlinks. Keep in mind
if your user is part of the Administrators group and UAC is enabled you will
still need to start your shell using Run as Administrator
.
Issues With npm: EEXISTS
, Path too Long, etc
There were always two major issues with running Node.js on Windows: first and
foremost, the operating system maintains a maximum length for path names, which
clashes with Node’s traditional way of nesting modules in node_modules
. The
second issue was a bit more subtle: The npm installer had a set of steps it
executed for each package and it would immediately start executing them as soon
as it decided to act on a package resulting in hard-to-debug race conditions.
npm
3 is a nearly complete rewrite of npm
, fixing both issues. Windows users of
Ember Cli might want to make the switch to npm
3 to benefit from its
flat module installation (solving most issues involving long path names) as well
as its multi-stage installer.
Editors
Visual Studio Code
If you are using VSCode with ember-cli
, there’s an official
extension pack
maintained by the Ember Learning team that adds multiple ember plugins that can help in
Ember development. If you already have VSCode installed on your machine, you can
click here to view this extension pack inside VSCode. Alternatively, you can
also search for emberjs.emberjs
inside the extensions view.
Atom
If you are using Atom with ember-cli
, there are some
packages available specific to Ember development.
Atom -> Preferences -> Install
- ember-cli-helper - ember-cli integration in Atom
- ember-tabs - Makes atom.io work better with Ember pods
- atom-ember-components - See all controllers and components that are rendering your component. Currently only works with pods structure.
Emacs
If you are using Emacs with ember-cli
,
Emacs creates temporary backup, autosave, and lockfiles that interfere with
broccoli watcher, so they need to either be moved out of the way or disabled.
To do that, ensure your emacs configuration contains the following:
(setq backup-directory-alist `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))
(setq create-lockfiles nil)
An ember-mode package is also
available. It has shortcuts for quickly navigating files in ember projects,
running generators, and running build, serve, and test tasks. It also includes
support for linking build errors to files and minibuffer notifications of
ember serve
status. It can be installed from MELPA. To
use MELPA, ensure your configuration contains the following:
(require 'package)
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/") t)
(package-initialize)
Then ember-mode can be installed from the package menu at M-x
package-list-packages
. After it is installed, add a file named
.dir-locals.el
to the root of your ember projects with the contents:
((nil . ((mode . ember))))
to enable it inside those projects.
Sublime Text
If you are using Sublime Text with ember-cli
,
by default it will try to index all files in your tmp
directory for its
GoToAnything functionality. This will cause your computer to come to a
screeching halt @ 90%+ CPU usage, and can significantly increase build times.
Simply remove these directories from the folders Sublime Text watches:
Sublime Text -> Preferences -> Settings - User
// folder_exclude_patterns and file_exclude_patterns control which files
// are listed in folders on the side bar. These can also be set on a per-
// project basis.
"folder_exclude_patterns": [".svn", ".git", ".hg", "CVS", "tmp/class-*", "tmp/es_*", "tmp/jshinter*", "tmp/replace_*", "tmp/static_compiler*", "tmp/template_compiler*", "tmp/tree_merger*", "tmp/coffee_script*", "tmp/concat-tmp*", "tmp/export_tree*", "tmp/sass_compiler*"]
WebStorm
If you are using WebStorm with
ember-cli
, you will need to modify your .gitignore
file, enable
ECMAScript6
settings, and mark certain directories.
First, add the following line to .gitignore
:
.idea
Next, from the WebStorm menu:
File > Settings -> Languages & Frameworks -> JavaScript -> ECMAScript6
Click ‘OK’ to close the Settings modal window.
Next, in Webstorm’s Project window right-click on each of the following directories, go to ‘Mark Directory As’ and mark as indicated:
Mark as Excluded
:
/tmp
/dist
Mark as Resource Root
:
/
/bower_components
/bower_components/ember-qunit/lib
/public
Mark as Test Sources Root
:
/tests
Intellij-emberjs
This plugin provides excellent Ember.js support for all JetBrains IDEs that support JavaScript, including WebStorm.
In order to install it, go to File | Settings... | Plugins | Browse
repositories...
search for Ember.js
and hit the Install button.
Vim
If you are using Vim with ember-cli
, Vim creates
temporary backups and autosaves which interfere with broccoli, so they need to
either be moved out of the way or disabled. To do that, ensure your .vimrc
contains the following:
set backupdir=~/.vim/backup//
set directory=~/.vim/swap//
set undodir=~/.vim/undo//
And make sure to create the directories- mkdir -p ~/.vim/backup; mkdir -p ~/.vim/swap; mkdir -p ~/.vim/undo
Some useful Vim plugins for working with Ember.js:
- ember_tools - Provides various tools for navigation and code reformatting, similar to rails.vim for Rails.
- projectionist - Powerful project navigation, provided you write your own code projections. Here’s an example
tutorials
Getting Started
Tooling
Testing
Addons
- Introducing Ember CLI Addons
- Building Ember CLI Addons Simply
- Updating EmberCLI addons to the latest ember-cli (Video)