Skip to content

Instantly share code, notes, and snippets.

@tsubomii
Last active June 8, 2017 02:03
Show Gist options
  • Save tsubomii/8c67d0267c092bbc83cefa43b40e0b89 to your computer and use it in GitHub Desktop.
Save tsubomii/8c67d0267c092bbc83cefa43b40e0b89 to your computer and use it in GitHub Desktop.
The ember-cli-fastboot 1.0 migration guide for ember addon authors.

Required dependency update

ember-cli-fastboot 1.0 requires ember-cli version above 2.14.0

Testing guidelines

When you make the changes to make sure your addons are backward compatible for upcoming FastBoot build changes, make sure to test all the usecases as follows:

  1. An app running the current FastBoot double builds. Your app should boot and function correctly in browser and FastBoot.
  2. An app running with the proposed FastBoot build changes here. Your app should boot and function correctly in browser and FastBoot.
  3. An app having ember-cli-fastboot not installed. Make sure the fastboot initializers or vendor files are not running in browser.

What has changed

  1. process.env.EMBER_CLI_FASTBOOT no longer exists
  2. The following addon folder structure will no longer be supported app/(instance-)?initializer/browser/<someName>.js and app/(instance-)?initializer/fastboot/<someName>.js

What you need to do to convert your fastboot addon to be compatible with latest build

From high level, in fastboot 1.0 you will get one single build output, it's used for both browser and fastboot. The changes for addons are categoried as following

  1. Remove reference to process.env.EMBER_CLI_FASTBOOT variable, transform the javascript files that only need to be loaded into browser to be wrapped into if (typeof FastBoot === 'undefined) check. Please see code example 1 below
  2. Use new ember-cli API updateFastBootManifest to load javascript only needed in fastboot environment. To execute ember app in node environment, fastboot will evaluate an array of vendorFiles, before run your ember app code. This updateFastBootManifest provide a way for you to add more javascripts into that vendorFiles array, by default only vendor.js
  3. Flattern any folder structure like app/(instance-)?initializer/browser/<someName>.js and app/(instance-)?initializer/fastboot/<someName>.js, basically get rid of the level browser or fastboot. Use if (typeof Fastboot === 'undefined') check to wrap code inside initializer only should run in browser. See conversion example 3 below. Check if (typeof Fastboot !== 'undefined') for initializer only should run in fastboot.

Understand ember-cli hook and basic about fastboot

include() ember-cli hook

Addon can specify what additional assets to be append into vendor.js using app.import inside include function. The common senario is we import assets from vendor directory or bower_component or node_modules

app.import(<same asset path>, {outputFile: 'assets/fastboot-file.js')

when we specify outputFile option, instead of append the asset into vendor.js (default behavior). import api outputs the assets into the path specify in outputFile option. This feature can be used when we want to load assets from dist/assets directory for fastboot.

treeForVendor

This api is a hook that we can implement additional transformation to other asset tree and merge them with the default vendor tree. We can consider it's a middleware between reading vendor.js from file system and the final output of vendor tree for build pipeline. If we don't implement additional logic here, the vendor.js contain will be the output of vendor tree. In following example we add conditional check for assets only suppose to run in browser, see conversion code example 1 below. Please note additional asset tree merged into vendor tree needs to be explicted app.import('venor/assetname')

updateFastBootManifest

This API give us oppotunity to include additional asset into fastboot environment only. By default if assets is inside addons' vendor or public directory, we can refer to it using path addonName/assetFileName. However if we want to load file from dist/assets directory, we can use app.import outputFile option to write file into dist/assets directory then add it into vendorFiles array with relative path assets/assetFileName. See code example 2 below.

Code Example for how to convert existing addons.

Following are the usecases where addons will break with the upcoming FastBoot build changes.

Note: One or more of the below usecases may apply for your addon. Make sure to fix for each usecase.

Using process.env.EMBER_CLI_FASTBOOT to run import in browser build

Example of current situation

Let's say you addon does the following during build time:

treeForVendor(defaultTree) {
  var browserVendorLib = new Funnel(...);
  
   return new mergeTrees([defaultTree, browserVendorLib]);
}

included() {
   if (!process.env.EMBER_CLI_FASTBOOT) {
     // only when it is not fastboot build
     app.import('vendor/<browserLibName>.js');
   }
}

What happens post FastBoot build issues are resolved?

This environment variable will no longer be available. Also FastBoot will load the same assets in sandbox that are sent to the browser in addition to some extra ones.

What should I do be backward compatible?

Instead of relying on process.env.EMBER_CLI_FASTBOOT and conditionally importing do the following:

a. Your file that is being imported should be wrapped in an if (typeof FastBoot === 'undefined) check

b. Remove the if (!process.env.EMBER_CLI_FASTBOOT) {..} and simply import the vendor file

Example of proposed change

Using the above example, your addon code should be changed to:

//conversion example 1a
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');
}

We can simplify above code by using fastboot-transform addon

//conversion example 1b
var fastboot-transform = require('fastboot-transform');

treeForVendor(defaultTree) {
  var browserVendorLib = new Funnel(...);
  return new mergeTrees([defaultTree, fastboot-transform(browserVendorLib)]);
}

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

Using process.env.EMBER_CLI_FASTBOOT to run import in fastboot build

Example of current situation

Let's say you addon does the following during build time:

treeForVendor(defaultTree) {
  var fastbootVendorLib = new Funnel(...);
  
   return mergeTrees([defaultTree, fastbootVendorLib]);
}

included() {
   if (process.env.EMBER_CLI_FASTBOOT) {
     // only when it is not fastboot build
     app.import('vendor/<fastbootLibName>.js');
   }
}

What happens post FastBoot build issues are resolved?

This environment variable will no longer be available. Also FastBoot will load the same assets in sandbox that are sent to the browser in addition to some extra ones. You want this extra vendor file to continue loading and eval'd in the FastBoot sandbox.

What should I do be backward compatible?

Instead of relying on process.env.EMBER_CLI_FASTBOOT and conditionally importing do the following:

a. ember-cli-fastboot@1.0.0-beta.18 exposes an API called updatedFastBootManifest that allows you to update additional vendorFiles to load.

b. Remove import conditionally and follow the API usage here

Example of proposed change

Using the above example, your addon code should be changed to:

//conversion example 2
treeForVendor(defaultTree) {
  var fastbootVendorLib = new Funnel(...);
  
   return mergeTrees([defaultTree, fastbootVendorLib]);
}

included() {
    app.import(fastbootVendorLib, {outputFile: 'assets/fastboot-lib.js'});
}

updateFastBootManifest(manifest) {
  // you can decide on the order of control (whether to push or unshift)
  // once the build is done you will automically see `fastbootVendorLib` file under `dist/<addonname>` since the src library is in 
  // <addon>/public
  manifest.vendorFiles.push('assets/fastboot-lib.js');
  
  return manifest;
}

Addon contains browser based initializers as app/(instance-)?initializers/browser/*js

Example of current situation

Let's say your addon needs a initializer that only should run in the browser:

Your browser initializer will be in a directory structure as : app/(instance-)?initializers/browser/<someName>.js and having content as follows:

export default {
  name: 'someName',
  initialize: function(app) {
   // some initializer code
  }

What happens post FastBoot build issues are resolved?

The new FastBoot build no longer has the notion of forking and creating assets for two different environments: browser and fastboot. Therefore, it is no longer going to filter these initializers and include them based on the which asset build is running. If you don't do the migration, your browser initializer will run the FastBoot environment and most likely your server side rendering would fail.

What should I do be backward compatible?

Instead of forking and filtering the intializers, do the following:

  1. Move the app/(instance-)?initializer/browser/<someName>.js -> app/(instance-)?initializers/<someName>.js
  2. The initialize function should be wrapped with if (typeof FastBoot === 'undefined') {...} check. See example below
  3. If during the build you are calling fastboot-filter-initializers you no longer should call it since it will be a no-op.

Example of proposed change

Using the above proposed changes, your new initializer located at app/(instance-)?initializers/<someName>.js should look as follows:

//conversion example 3
export default {
  name: 'someName',
  initialize: function(app) {
    if (typeof FastBoot === 'undefined') {
      // some initializer code
    }
  }

Addon containing fastboot based initializers as app/(instance-)?initializers/fastboot/*.js

Example of current situation

Let's say your addon needs a initializer that only should run in fastboot:

Your fastboot initializer will be in a directory structure as : app/(instance-)?initializers/fastboot/<someName>.js and having content as follows:

export default {
  name: 'someName',
  initialize: function(app) {
   // some initializer code
  }

What happens post FastBoot build issues are resolved?

The new FastBoot build no longer has the notion of forking and creating assets for two different environments: browser and fastboot. Therefore, it is no longer going to filter these initializers and include them based on the which asset build is running. If you don't do the migration, your fastboot initializer will end up running in the browser app and breaking your app.

What should I do to be backward compatible?

You don't need to do much except:

  1. If your addon is not calling fastboot-filter-initializers, please go ahead and use it to make sure it gets filtered by your addon.

NOTE: We didn't ask you to move this initializer as in the pervious usecase. The reason for this is we will do the auto-migration for you. Eventually once FastBoot 1.0 or 2.0 is released, you will need to move it to fastboot/(instance-)?intializers/<somename>.js since we will drop the auto-migration at some point (TBD).

When your addon only wants to support FastBoot 1.0, you will need to copy over your existing app/(instance-)?intializers/<somename>.js to fastboot/(instance-)?intializers/<somename>.js.

Addon contains app-lt-2-9 structure and imports initializers based on ember version

< TODO add more details with new API and backward compatible changes>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment