Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc updates to prepare for FastBoot 1.0 #70

Merged
merged 3 commits into from
Jun 8, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 121 additions & 22 deletions markdown/docs/addon-author-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ available.
The first step towards FastBoot support is ensuring that your addon
doesn't cause the app to crash when it is run in Node. You can verify this by
adding your addon to a new Ember app, adding the FastBoot addon, and
running the FastBoot server with `ember fastboot`.
running the FastBoot server with `ember serve` (provided your app is running
`ember-cli` 2.12.0 and above).

If that process results in an error and the server crashing, you know
that your addon is trying to do something that may work in the browser
Expand All @@ -37,19 +38,21 @@ Node, this value is not set.

### Tracking It Down

FastBoot works by turning an Ember application and all of its
dependencies, including your addon, into a single bundle of JavaScript.
Similar to how you ship an `app.js` and `vendor.js` file to the browser
when you deploy an Ember app, the FastBoot server also loads these two
files.
FastBoot works by running a singular build as it was running without the FastBoot
addon being installed. It basically loads the same assets `app.js` and `vendor.js`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this could be simplified a bit.

FastBoot works by running a singular build of your application, leveraging the common `app.js` and `vendor.js` files on the server and browser clients. However, on the server an additional asset `app-fastboot.js` will leveraged. This asset contains FastBoot specific overrides defined by the application or other addons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds much better :). Just tweaked the "server and browser clients" to call "browser and Node" since everywhere else we refer it as Node.

in its sandbox as they were shipped to the browser. In addition to loading the same
browser assets, it also generates `app-fastboot.js` which is an additive asset loaded
in FastBoot containing the FastBoot specific overrides defined by the app or other addons.
An addon can chose to load additional assets in the FastBoot using FastBoot's build
hooks described below.

What this means in practice is that errors will include stack traces
with very large line numbers. The code from your addon will be intermingled
with other addons, and Ember itself, in the `vendor.js` file.

To find the cause of the exception, look at the stack trace shown when
running the app. Note the line number, then open
`dist/fastboot/vendor.js` in your text editor. Scroll down to the
`dist/assets/vendor.js` in your text editor. Scroll down to the
provided line number (or, better, use your editor's "Go to Line"
functionality) to pinpoint exactly which line is causing the issue.

Expand Down Expand Up @@ -190,33 +193,129 @@ If your addon imports third-party code and you are unable to make
changes to it to add Node compatibility, you can add a guard to your
`ember-cli-build.js` file to only include it in the browser build.

The FastBoot addon sets the `EMBER_CLI_FASTBOOT` environment variable
when it is building the FastBoot version of the application. Use
this to prevent the inclusion of a library in the FastBoot build:
The FastBoot addon simply provides the FastBoot server a manifest of

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could 'simply' be removed from this sentence without changing the overall meaning?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

assets to load in the Node server. If your third-party library is not
Node compatible, you can wrap it with a check as below in an addon:

```js
if (!process.env.EMBER_CLI_FASTBOOT) {
// This will only be included in the browser build
app.import('some/jquery.plugin.js')
var map = require('broccoli-stew').map;

treeForVendor(defaultTree) {
var browserVendorLib = new Funnel(...);

browserVendorLib = map(browserVendorLib, (content) => `if (typeof FastBoot === 'undefined') { ${content} }`);

return new mergeTrees([defaultTree, browserVendorLib]);
}

included() {
// this file will be loaded in FastBoot but will not be eval'd
app.import('vendor/<browserLibName>.js');
}
```

Note that not including the library in the FastBoot build means that any
Note that not running the library in the FastBoot build means that any
modules it exports will not be available inside the app when running in
the FastBoot environment. Make sure to guard against missing
dependencies in that case.

## Loading additional assets in FastBoot

Often your addon may require to load libraries that are specific to the
FastBoot environment and only need to be loaded on the server side. This
can include loading libraries before or after the vendor file is loaded
in the sandbox and/or before or after the app file is loaded in the sandbox.
Since the FastBoot manifest defines an array of vendor and app files to load
in the sandbox, an addon can define additional vendor/app files to
load in the sandbox as well.

If your addon requires to load something in the sandbox: you can define
the `updateFastBootManifest` hook from your addon (in `index.js`):

```js
updateFastBootManifest(manifest) {
/**
* manifest is an object containing:
* {
* vendorFiles: [<path of the vendor file to load>, ...],
* appFiles: [<path of the app file to load>, ...],
* htmlFile: '<path of the base page that should be served by FastBoot>'
* }
*/

// This will load the foo.js before vendor.js is loaded in sandbox
manifest.vendorFiles.unshift('<path to foo.js under dist>');
// This will load bar.js after app.js is loaded in the sandbox
manifest.appFiles.push('<path to bar.js under dist>');

// remember to return the updated manifest, otherwise your build will fail.
return manifest;
}
```

## Conditionally include assets in FastBoot asset

Often your addon may need to conditionally include additional app trees based on ember version. Example, Ember changed an API and in order to have your addon be backward compatible for the API changes you want to include an asset when the ember version is x. For such usecases you could define the `treeForFastBoot` hook in your addon's `index.js` as below:

```js
treeForFastBoot: function(tree) {
let fastbootHtmlBarsTree;

// check the ember version and conditionally patch the DOM api
if (this._getEmberVersion().lt('2.10.0-alpha.1')) {
fastbootHtmlBarsTree = this.treeGenerator(path.resolve(__dirname, 'fastboot-app-lt-2-9'));
return tree ? new MergeTrees([tree, fastbootHtmlBarsTree]) : fastbootHtmlBarsTree;
}

return tree;
},
```

The `tree` is the additional fastboot asset that gets generated and contains the fastboot overrides.

## Browser-Only or Node-Only Initializers

If your addon requires browser-specific or Node-specific initialization
code to be run, consider using the
[fastboot-filter-initializers](https://github.com/ronco/fastboot-filter-initializers)
Broccoli plugin. This allows you to define target-specific
initializers just by putting them in the right directory.

For example, to write an instance initializer that only runs in the
FastBoot environment, you'd simply put it in
`app/instance-initializers/fastboot`.
code to be run, you would need to define them seperately as follows:

1. Browser-Only initializers

You should define an initializer under `app/initializers` or `app/instance-initializers` as normally
you would do. The only addition would to wrap the initializer with a FastBoot environment check. This is
to make sure the initializer does not run in FastBoot. An example is as follows:

```js
function initialize(app) {
if (typeof FastBoot === 'undefined') {
// only execute this in browser
}
}

export default {
name: 'my-initializer',
initialize: initializer
}
```

2. Node-Only initializer

Often you want to define Node specific behavior for your app at boot time. You should define the Node-Only
initializer under `fastboot/initializers` or `fastboot/instance-initializers`. Note the `fastboot` directory which is a sibling of the `app` or `addon` directory. The FastBoot addon will read the `fastboot`
directories from all addons and your parent app and create `app-fastboot.js` which is included in the FastBoot manifest and loaded in FastBoot only. This is never shipped to the browser. An example of directory structure is as follows:

```js
-+ app/
----+ initializers/
--------+ foo1.js
--------+ ...
----+ instance-initializers/
--------+ foo.js
--------+ ...
-+ fastboot/
----+ initializers/
--------+ foo-fastboot.js
--------+ bar.js
```

## Requiring Node Modules

Expand Down
2 changes: 0 additions & 2 deletions markdown/docs/deploying.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ Lambda ember-cli-deploy plugin](https://github.com/bustlelabs/ember-cli-deploy-f

## Custom Server



If one of the out-of-the-box deployment strategies doesn't work for you,
you can adapt [the standalone FastBoot
server](https://github.com/ember-fastboot/ember-fastboot-server) to your
Expand Down
70 changes: 27 additions & 43 deletions markdown/docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,26 @@ This will install the `ember-cli-fastboot` addon via npm and save it to your app
You can start a FastBoot server on your development machine by running:

```sh
ember fastboot
ember serve
```

This starts a FastBoot server listening on port 3000\.
This starts a FastBoot server listening on port 4200\.
You can verify it's working by curling from `localhost`:

```sh
curl http://localhost:3000
curl 'http://localhost:4200/' -H 'Accept: text/html'
```

You should see the content of your application's index template rendered in the output, instead of the empty HTML most client-side rendered apps show.

To stop the server, press `Ctrl-C` on your keyboard to kill the process.

Note that, at the moment, the server does not automatically restart when you make changes.
If you make changes to your application, you'll need to kill the server and restart it.

You can run the development server on a different port by passing the `--port` argument:
You can run the development server on a different port by passing the `--port` argument or use any of `ember-cli` options:

```sh
ember fastboot --port 4567
ember serve --port 4567
```

*Note*: `ember fastboot` command is soon going to be deprecated.

## Testing Locally using `ember serve`

If your app is running ember-cli 2.12.0-beta.1 and above, you can now serve your FastBoot rendered content with `ember serve` as well. Moreover, all options of `ember serve` will work with FastBoot (example `--proxy`, `--port`, `--live-reload` etc). The Node server will automatically restart when you make changes.

In order to serve the CSS, JavaScript, images in addition to rendering the server side content, just run:

```sh
ember serve
```

View the FastBoot-rendered by visiting [localhost:4200](http://localhost:42000/). You can alternatively also use the following curl command: `curl 'http://localhost:4200/' -H 'Accept: text/html'`.

### Disabling FastBoot

You can also turn off the server side rendering on a per request basis using `fastboot` query parameter. To disable FastBoot rendered content, visit [localhost:4200/?fastboot=false](http://localhost:4200/?fastboot=false). You can enable FastBoot rendered content again by visiting [localhost:4200/?fastboot=true](http://localhost:4200/?fastboot=true).
Expand All @@ -94,7 +77,7 @@ As with a standard Ember build for the browser, compiled assets by default go in
FastBoot adds two additions:

1. `dist/package.json`, which contains metadata about your app for consumption by the FastBoot server.
2. `dist/fastboot/`, which contains your app's compiled JavaScript that is evaluated and run by the FastBoot server.
2. `dist/assets/`, which contains your app's compiled JavaScript that is evaluated and run by the FastBoot server (`vendor.js`, `app.js`, `app-fastboot.js`).

For more information, see the [Architecture](#architecture) section.

Expand Down Expand Up @@ -415,31 +398,31 @@ export default Ember.Route.extend({

## Useful Ember Addons for FastBoot

### ember-network: Fetch Resources Over HTTP (AJAX)
### ember-fetch: Fetch Resources Over HTTP (AJAX)

JavaScript running in the browser relies on the `XMLHttpRequest` interface to retrieve resources, while Node offers the `http` module.
What do we do if we want to write a single app that can fetch data from our API server when running in both environments?

One option is to use the [ember-network] addon, which provides an implementation of [Fetch API][fetch-api] standard that works seamlessly in both environments.

To use `ember-network`, install it as you would any addon:
To use `ember-fetch`, install it as you would any addon:

```sh
ember install ember-network
ember install ember-fetch
```

Once installed, you can import it using the JavaScript module syntax, wherever you need to make a network request:

```javascript
import fetch from 'ember-network/fetch';
import fetch from 'ember-fetch/fetch';
```

The `fetch()` method returns a promise and is very similar to jQuery's `getJSON()` method that you are likely already using.
For example, here's an Ember route that uses `fetch()` to access the GitHub JSON API and use it as the route's model:

```javascript
import Route from 'ember-route';
import fetch from 'ember-network/fetch';
import fetch from 'ember-fetch/fetch';

export default Route.extend({
model() {
Expand All @@ -451,7 +434,7 @@ export default Route.extend({
});
```

For more information, see [ember-network] and the [Fetch documentation on MDN][fetch-api].
For more information, see [ember-fetch](https://github.com/stefanpenner/ember-fetch) and the [Fetch documentation on MDN][fetch-api].

### ember-cli-document-title: Specify the Page Title

Expand All @@ -472,7 +455,7 @@ export default Ember.Route.extend({
});
```

See [ember-cli-document-title] for more information.
See [ember-cli-document-title](https://github.com/kimroen/ember-cli-document-title) for more information.

### ember-cli-head: Enable Open Graph, Twitter Cards and Other `<head>` Tags

Expand Down Expand Up @@ -514,7 +497,7 @@ export default Ember.Route.extend({
});
```

For more information, see [ember-cli-head].
For more information, see [ember-cli-head](https://github.com/ronco/ember-cli-head).

## Tips and Tricks

Expand Down Expand Up @@ -650,9 +633,9 @@ Any code in these hooks will be run inside of FastBoot and should be free of ref
FastBoot relies on [`Ember.ApplicationInstance`](http://emberjs.com/api/classes/Ember.ApplicationInstance.html) to execute your Ember application on the server.
jQuery is [disabled](https://github.com/emberjs/ember.js/blob/v2.7.0/packages/ember-application/lib/system/application-instance.js#L370) by default for these instances because most of jQuery depends on having full DOM access.

### Use `ember-network` for XHR requests
### Use `ember-fetch` for XHR requests

If you are depending on jQuery for XHR requests, use [ember-network](https://github.com/tomdale/ember-network) and replace your `$.ajax` calls with `fetch` calls.
If you are depending on jQuery for XHR requests, use [ember-fetch](https://github.com/stefanpenner/ember-fetch) and replace your `$.ajax` calls with `fetch` calls.

## Troubleshooting in Node

Expand All @@ -663,7 +646,7 @@ Because your app is now running in Node, not the browser, you will need a new se
Enable verbose logging by running the FastBoot server with the following environment variables set:

```sh
DEBUG=ember-cli-fastboot:* ember fastboot
DEBUG=ember-cli-fastboot:* ember serve
```

Pull requests for adding or improving logging facilities are very welcome.
Expand All @@ -688,14 +671,14 @@ node-inspector --no-preload

Once the debug server is running, you'll want to start up the FastBoot server with Node in debug mode.
One thing about debug mode: it makes everything much slower.
Since the `ember fastboot` command does a full build when launched, this becomes agonizingly slow in debug mode.
Since the `ember serve` command does a full build when launched, this becomes agonizingly slow in debug mode.

### Speeding up Server-side Debugging

Avoid the slowness by manually running the build in normal mode, then run FastBoot in debug mode without doing a build:

```sh
ember build && node --debug-brk ./node_modules/.bin/ember fastboot --no-build
ember build && node --debug-brk ./node_modules/.bin/ember serve
```

This does a full rebuild and then starts the FastBoot server in debug mode.
Expand Down Expand Up @@ -745,13 +728,14 @@ That directory contains everything you need to make your application work in the
You can upload it to a static hosting service like S3 or Firebase, where browsers can download and run the JavaScript on the user's device.

FastBoot is a little different because, instead of being purely static, it renders HTML on the server and therefore needs more than just static hosting.
We need to produce a build of the Ember app that's designed to work in Node rather than the browser.
We need to build additional assets that's designed to work in Node rather than the browser.

When you run `ember build`, your FastBooted app will produce different artifact sets in `dist/` and `dist/fastboot`.
The artifacts in `dist` are client-side copies of your application that will be sent to all browsers that request your application.
The artifacts in `dist/fastboot` are used by the FastBoot Server to SSR your page.
When you run `ember build`, your FastBooted app will produce `app.js` and `vendor.js` in `dist/assets` that are used in the browser and will produce an additive asset containing FastBoot overrides for your app in `app-fastboot.js` in `dist/assets`.
The artifacts in `dist/assets` are client-side copies of your application that will be sent to all browsers that request your application.
The artifacts for your FastBoot server are defined by a `manifest` key entry in `dist/package.json` which
will minimally contain `vendor.js`, `app.js` and `app-fastboot.js`.

You can test that the process is working (and that your app is FastBoot-compatible) by running `ember fastboot`, which builds your app then starts up a local server.
You can test that the process is working (and that your app is FastBoot-compatible) by running `ember serve`, which builds your app then starts up a local server and renders your app on server side.

Once you've confirmed everything looks good, it's ready to hand off to the FastBoot server running in the production environment.
The good news is that this process is usually handled for you by a deployment plugin for `ember-cli-deploy`; see [Deploying](/docs/deploying) for more information about different deployment strategies.
Expand All @@ -764,8 +748,8 @@ The server offers an [Express middleware][express] that can be integrated into a

```javascript
var server = new FastBootServer({
appFile: appFile,
vendorFile: vendorFile,
appFiles: [appFile, appFile-fastboot],
Copy link
Contributor

@stefanpenner stefanpenner May 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

appFile-fastboot is likely not correct, as it is not a valid variable name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohoo yeah, I meant appFastBootFile to denote the additional asset for overrides

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanpenner fixed.

vendorFiles: [vendorFile],
htmlFile: htmlFile,
ui: ui
});
Expand Down
6 changes: 4 additions & 2 deletions markdown/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ For more information, see [the User Guide](/docs/user-guide)
To start the FastBoot server during development:

```sh
ember fastboot
ember serve
```

For more information, see the [Quickstart](/quickstart).
Expand All @@ -30,4 +30,6 @@ The best way to get help is via the [Ember Community Slack](https://ember-commun

### The Road to FastBoot 1.0

FastBoot is currently pre-1.0 software and under active development. You can track progress to FastBoot 1.0 by visiting the [Road to 1.0](https://github.com/tildeio/ember-cli-fastboot/issues/98) GitHub issue.
FastBoot is currently pre-1.0 software and under active development. You can track progress to FastBoot 1.0 by visiting the [Road to 1.0](https://github.com/ember-fastboot/ember-cli-fastboot/issues/396) GitHub issue.

As addon authors or if you wish to contribute please help us in shipping in FastBoot 1.0 with helping fix addons listed in this [issue](https://github.com/ember-fastboot/ember-cli-fastboot/issues/387).
Loading