Loading Ember CLI Addons dynamically on application start


#1

I’m currently investigating how I could load Ember CLI Addons dynamically (dynamically meaning I want to decide which addons to load when I serve the index.html). The idea is to have a base application with a certain feature set and depending on the capabilities of the device (the project is a device configuration UI where index.html is served directly from the device) dynamically load other JS files that define more routes, controller, templates etc. for features that only certain devices are capable of.

Of course I can simply change the default

<script src="assets/vendor.js"></script>
<script src="assets/<app-name>.js"></script>

to

<script src="assets/vendor.js"></script>
<script src="assets/<addon-name>.js"></script>
<script src="assets/<app-name>.js"></script>

The problem is I cannot build that assets/<addon-name>.js deliverable with Ember CLI as I have to install the Addon into the project where it automatically gets built into assets/<app-name>.js. The way I currently do it I build the application once without the Addon installed and once with the Addon installed and then take the diff of the 2 deliverables as assets/<addon-name>.js. Of course that’s not really a great way to build an app.

I was wondering whether other people have had projects with similar requirements or wether there are other ways to solve this that I didn’t think of yet. I think this might also be something that could be interesting to support in some way in Ember CLI (I think there’s already some discussion going on on the topic os splitting up an application into multiple JS files?).


#2

We have a similar requirement. Did you find a solution for it?

We basically have an addon that’s not consumed by an ember-cli app. So instead of building the addon as part of vendor.js when running ember build --environment production we need to split the vendor code and the addon code, so then the consumer app can simply refer to assets/addon-name.js.

Anyway, let me know if you found a way to do it. I’ll update this thread once I get this.


#3

The only solution I found is to build the app once with the addon and once without it and then use the diff of both builds. That should work in your case as well. However, it’s not really a good solution of course.

I think that there’s an open issue on Ember CLI though that tackles the problem of splitting an app’s deliverable into multiple files. I don’t think much has happened there though.


#4

I’ll have a look at the issues to see if I can find something.

I found this blogpost from pixelhandler, that talks about building standalone addons. Some of the conepts are based on how liquid-fire does it (see packaging). I’ll give it a go now and see if it works.


#5

The liquid-fire example really helped. In our case it was a bit simpler. Added a bunch of new dependencies to packages.json. Then simply get the addon, app, templates and compile the templates. We already had another file (tyrion/tyrion-components) that was adding the references to the components, so we didn’t need to add the glue and registrations.

var mergeTrees = require('broccoli-merge-trees');
var pickFiles = require('broccoli-static-compiler');
var compileES6 = require('broccoli-es6-concatenator');
var templateCompiler = require('broccoli-ember-hbs-template-compiler');
var es3Safe = require('broccoli-es3-safe-recast');
var Funnel = require('broccoli-funnel');
var wrap = require('./wrap');

var addonTree = pickFiles('../addon', {srcDir: '/', destDir: 'tyrion'});
var appTree = pickFiles('../app', { srcDir: '/', destDir: 'app'});

var templateTree = templateCompiler('../app/templates');
templateTree = pickFiles(templateTree, {srcDir: '/', destDir: 'app/templates'});

var jsTree = mergeTrees([addonTree, appTree, templateTree], {overwrite: true});

var compiled = compileES6(jsTree, {
  wrapInEval: false,
  inputFiles: ['tyrion/tyrion-components.js'],
  ignoredModules: ['ember', 'tyrion'],
  outputFile: '/tyrion.js',
  legacyFilesToAppend: ['app/templates/**/*.js']
});
compiled = wrap(compiled);

var css = new Funnel('../vendor', {
  include: [/\.css$/],
  getDestinationPath: function(relativePath) {
    if (relativePath === 'tyrion.css') {
      return 'tyrion.css';
    }
    return relativePath;
  }
});

module.exports = mergeTrees([es3Safe(compiled), css]);

Hope this helps.

Also this seems like a common pattern. I’ll look into making this part of addon. Maybe even ember build could generate this by default, re-using some of the existing trees to avoid dup work.