Beyond async & defer: Javascript Dependency Handling with system.js

Today, after another depressing look at Google Pagespeed results, I went looking for a hands-down dead simple approach to javascript dependency management in the browser.

I looked at require.js, webpack and browserify. All three of them seemed to be heavily focussed on nodejs and it looked like it wouldn’t work out of the box in three simple steps.

Somehow I came across system.js and decided to give it a try. It looked clean and simple. Three hours later, here is what I found out about it.

Getting system.js is super easy (if you have bower):

bower install system.js --save

Loading (here into a Django template):

<script src="{% static 'vendor/system.js/dist/system.js' %}"></script>

Configuration

    <script>
        System.config({
                        defaultJSExtensions: "js",
            // set our baseURL reference path
            baseURL: '{% static '' %}',
            map: {
                "jquery": 'js/libs/jquery.min.js',
                "bootstrap": 'js/libs/bootstrap.min.js',
                "scrollmagic": 'vendor/scrollmagic/scrollmagic/minified/ScrollMagic.min.js'
            },
                      meta: {
                bootstrap: {
                                        format: 'global',
                    deps: ['jquery']
                }
            }
        });
    </script>

Inline Javascript:

Promise.all([
   // Asynchronously loads the javascript files if not already loaded on the current page
   // alternatively maybe use the bundle option: https://github.com/systemjs/systemjs/blob/master/docs/config-api.md#bundle
     // or see the dependency solution below 
   System.import('jquery'),
   System.import('scrollmagic'),
]).then(function (modules) {
   var jQuery = modules[0];
   var ScrollMagic = modules[1];

   jQuery(document).ready(function () {

      var controller = new ScrollMagic.Controller();
   });

The deps part in the meta configuration makes sure that bootstrap is working correctly (first, jQuery needs to be loaded, then the bootstrap jquery plugins need to be loaded by jQuery).

This allows for a one-import statement even for complex dependencies:

 // load jquery & bootstrap & execute them
Promise.all([
        System.import('bootstrap')
]).then(function () {
        jQuery(document).ready(function () {
                jQuery('#accordion').collapse();
});

Sometimes one wants to include javascript files that are written to be compatible with different formats (amd, es6, …). In that case system.js sometimes can’t figure out the right format and tries to import assumed dependencies it ‘finds’ inside those files. To prevent that, define the file’s format in the meta configuration like this:

meta: {
    bootstrap: {
            deps: ['jquery']
    },
    isotope: {
            format: 'global', // or if supported: format: 'amd',
            deps: ['jquery']
    },
}