-
Notifications
You must be signed in to change notification settings - Fork 49
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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` | ||
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. | ||
|
||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could 'simply' be removed from this sentence without changing the overall meaning? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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). | ||
|
@@ -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. | ||
|
||
|
@@ -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() { | ||
|
@@ -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 | ||
|
||
|
@@ -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 | ||
|
||
|
@@ -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 | ||
|
||
|
@@ -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 | ||
|
||
|
@@ -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. | ||
|
@@ -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. | ||
|
@@ -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. | ||
|
@@ -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], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ohoo yeah, I meant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stefanpenner fixed. |
||
vendorFiles: [vendorFile], | ||
htmlFile: htmlFile, | ||
ui: ui | ||
}); | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.