An open API service indexing awesome lists of open source software.

https://github.com/astronomersiva/ember-differential-bundles

An approach to serving differential bundles in Ember
https://github.com/astronomersiva/ember-differential-bundles

Last synced: about 2 months ago
JSON representation

An approach to serving differential bundles in Ember

Awesome Lists containing this project

README

        

# Serving differential builds with EmberJS

### Introduction

There is currently no way to do differential serving of assets
based on the user's browser in Ember land. The closest thing
currently is a [pre-RFC](https://github.com/emberjs/rfcs/issues/383).

This is one feasible solution to this problem. Importantly,

* This solution tries to address engines as well. With the usual
`type="module"` and `nomodule` approach, you cannot handle
engines as they rely on the asset manifest provided by a `meta` tag
with name as `app/config/asset-manifest`. There is no way to toggle
`meta` tags in the frontend as of now and until Ember Engines support
the `storeConfigInMeta` option, you will either have to do something
similar to this solution or not do differential serving of engine assets
alone. PS: There is a long-pending [PR](https://github.com/ember-engines/ember-engines/pull/228)
to implement the `storeConfigInMeta` option in Ember Engines.
* This solution requires you to serve different bundles by manipulating
the HTML at runtime in the server based on a cookie, or if possible,
parsing the UA.

### Steps

* In your `index.html`, add a `data-for="ember"` attribute to script tags
that are generated by Ember. For example,
```html


```
* In your application code, run a small script to set a cookie to determine
if the user is currently using a modern or legacy browser.
```javascript
// Serve modern build if Promise and async/await are present
let testCode = 'async() => { let p = new Promise(); await p(); }';

try {
// this needs to be a new Function/eval because otherwise,
// you will get Syntax Errors in the code
(new Function(testCode))();
// Set a never-expiring cookie
} catch(err) {
// Set a cookie with a short validity(say a month)
// If the user updates their browser, it will be reflected
// once the cookie expires
}
```
* In your `targets.js` file, use the `process.env.LEGACY` flag to toggle support
for legacy browsers.
```javascript
'use strict';

let browsers = [
'last 1 Chrome versions',
'last 1 Firefox versions',
'last 1 Safari versions'
];

const isCI = !!process.env.CI;
const isProduction = process.env.EMBER_ENV === 'production';
const isLegacyBuild = !!process.env.LEGACY;

if (isCI || isProduction || isLegacyBuild) {
browsers = [
'Chrome >= 42',
'Firefox >= 39',
'Edge >= 14',
'Safari >= 10'
'ie 11'
];

}

module.exports = {
browsers
};
```
* `broccoli-persistent-filter` [does not support](https://github.com/stefanpenner/broccoli-persistent-filter/issues/124)
concurrent builds. This will result in build errors with the message `Unexpected end of file ...`. To avoid this, in
`ember-cli-build.js`, set the `BROCCOLI_PERSISTENT_FILTER_CACHE_ROOT` environment variable to a random path. For
example,
```javascript
if (EmberApp.env() === 'production') {
process.env.BROCCOLI_PERSISTENT_FILTER_CACHE_ROOT = path.join(
process.cwd(),
`cache_${Date.now()}_${Math.floor(Math.random() * 10)}`
);
}
```
* Run parallel builds with
```bash
ember build --environment=production --output-path=modern &
LEGACY=true ember build --environment=production --output-path=legacy
```
* Run the [`index.js`](index.js) in this repo. It will generate a `dist`
folder with an `index.html` containing combined script tags and meta
tags wrapped in `` and `` tags.
* In your server, detect the presence of the cookie that was previously set
(or use the UA) and replace the tags accordingly.
```
// if modern browser
replaceAll('.*', '');
replaceAll('(.*)', '$1');

// if legacy browser
replaceAll('.*', '');
replaceAll('(.*)', '$1');
```