Creating a Plugin

A plugin is just a set of overrides for the loader hooks of the ES6 module specification.

The hooks plugins can override are locate, fetch, translate and instantiate.

Read more about loader extensions and hooks at the ES6 Module Loader polyfill wiki.

The behaviors of the hooks are:

  • Locate: Overrides the location of the plugin resource
  • Fetch: Called with third argument representing default fetch function, has full control of fetch output.
  • Translate: Returns the translated source from load.source, can also set load.metadata.sourceMap for full source maps support.
  • Instantiate: Providing this hook as a promise or function allows the plugin to hook instantiate. Any return value becomes the defined custom module object for the plugin call.

Building Plugins

When building via SystemJS Builder, plugins that use the translate hook will be inlined into the bundle automatically.

In this way, the bundle file becomes independent of the plugin loader and resource.

If it is desired for the plugin itself not to be inlined into the bundle in this way, setting exports.build = false on the plugin will disable this, causing the plugin loader itself to be bundled in production instead to continue to dynamically load the resource.

Sample CoffeeScript Plugin

For example, we can write a CoffeeScript plugin with the following (CommonJS as an example, any module format works fine):

js/coffee.js:

  var CoffeeScript = require('coffeescript');

  exports.translate = function(load) {
    // optionally also set the sourceMap to support both builds and in-browser transpilation
    // load.metadata.sourceMap = generatedSourceMap;
    return CoffeeScript.compile(load.source);
  }

By overriding the translate hook, we now support CoffeeScript loading with:

 - js/
   - coffee.js             our plugin above
   - coffeescript.js       the CoffeeScript compiler
 - app/
   - main.coffee
  System.import('app/main.coffee!').then(function(main) {
    // main is now loaded from CoffeeScript
  });

Source maps can also be passed by setting load.metadata.sourceMap.

Sample CSS Plugin

A CSS plugin, on the other hand, could override the fetch hook:

js/css.js:

  exports.fetch = function(load, fetch) {
    return new Promise(function(resolve, reject) {
      var cssFile = load.address;

      var link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = cssFile;
      link.onload = resolve;

      document.head.appendChild(link);
    })
    .then(function() {
      // return an empty module in the module pipeline itself
      return '';
    });
  }

Each loader hook can either return directly or return a promise for the value.

The other loader hooks are also treated otherwise identically to the specification.