Search code examples
ember.jsember-cliecmascript-6

Dynamic module import in Ember CLI


I have a bunch of modules defined in an Ember CLI app and each starts with the same path. I would like to import the modules into a module in the app. For example, I could write:

import post1 from 'posts/1';
import post2 from 'posts/2';
import post3 from 'posts/3';

export default Em.ObjectController.extend({
  posts: Em.A(post1, post2, post3),
});

However, I do not know the module names because they are created/named on the fly by a precompiler. All I know is that the path always begins with the same string. In this case, posts.

Is there a way to import all modules that begin with a particular path? For example, how can I do something like the following:

import posts from 'posts/*';

// or

registry['posts'].forEach(postId, i) {
  var path = 'posts/' + postId;

  import i from path;
}

Each of the modules I want to find and import has exported an object.

I have been through the ES6 module transpiler docs but can't find much.


Solution

  • The ES6 spec doesn't allow you to dynamically import modules using the import keyword. All importing and exporting using the module syntax must be done statically. However, it does provide a programmatic API that you can use to dynamically import modules. This article has a great summary of it (as well as a great summary of the rest of ES6 modules).

    My suggestion would be to wrap Ember's loader API in a ES6 compliant wrapper. For instance, Ember-CLI uses the require() function to get modules, so you could do something like this:

    window.System = window.System || {};
    window.System['import'] = function(moduleName) {
        return Ember.RSVP.Promise.resolve(window.require(moduleName));
    }
    
    // ...
    
    System.import('my-app/posts/' + postNumber).then(function(postModule) {
        // ...
    });
    

    You could also use the Ember require() function directly, my way just protects you in the likely event that Ember changes their module loader.