Declaring SystemJS modules

SystemJS is another module definition syntax and module loader that we should avoid at design time. We can say that SystemJS are an improved version as it is able to load any kind of module format and supports transpiration. This means that we can use plugins to load TypeScript files directly from the runtime environment, compile them and process the resulting modules on the fly.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 2.0 or higher, Gulp, SystemJS and the gulp-typescript plugin.

How to do it

If we have a ES6 module like the following one:

import { IArrays } from '../../interfaces';

class Arrays implements IArrays {
  // …
}

export default new Arrays();

We can compile the preceding into a SystemJS module using the module compilation flag as we can see in the following Gulp task:

gulp.task('build-es6-to-systemjs', function () {
    const tsProject = ts.createProject('tsconfig.json', {
        typescript: require('typescript'),
        module: 'system'
    });

    const input = './src/module_definitions/design_time/external/es6/*.ts';
    const output = './src/module_definitions/run_time/external/systemjs/';

    return gulp.src(input)
        .pipe(tsProject())
        .pipe(gulp.dest(output));
});

Note that the paths in the preceding Gulp tasks are the ones used in the companion source code. The ES6 modules are located un the the following module:

/src/module_definitions/design_time/external/es6/

The resulting UMD modules are located under the following folder:

/src/module_definitions/run_time/external/systemjs/

How it works

After compiling our ES6 module into JavaScript, the resulting SystemJS module will look as follows:

System.register([], function (exports_1, context_1) {
    "use strict";
    var __moduleName = context_1 && context_1.id;
    var Arrays;
    return {
        setters: [],
        execute: function () {
            Arrays = (function () {

                // ...

            }());
            exports_1("default", new Arrays());
        }
    };
}); 

The SystemJS modules are somehow similar to the AMD modules. SystemJS uses a function named register which is almost identical to the function named defined in AMD modules. Both functions take two arguments:

  • An array containing the paths of the dependencies of the module being defined
  • A callback function However, in SystemJS modules, the callback function doesn’t take the list of dependencies as its arguments. Another important difference is that instead of using the exports keyword, SystemJS modules return an object with two properties named execute and setters. Let’s take a look to the code generated for the ndrscr module to see the setters in action:
    System.register(["./arrays", "./collections", "./functions", "./objects", "./utility"], function (exports_1, context_1) {
      "use strict";
      var __moduleName = context_1 && context_1.id;
      var arrays_1, collections_1, functions_1, objects_1, utility_1, VERSION;
      return {
          setters: [
              function (arrays_1_1) {
                  arrays_1 = arrays_1_1;
              },
              function (collections_1_1) {
                  collections_1 = collections_1_1;
              },
              function (functions_1_1) {
                  functions_1 = functions_1_1;
              },
              function (objects_1_1) {
                  objects_1 = objects_1_1;
              },
              function (utility_1_1) {
                  utility_1 = utility_1_1;
              }
          ],
          execute: function () {
              exports_1("arrays", arrays_1.default);
              exports_1("collections", collections_1.default);
              exports_1("functions", functions_1.default);
              exports_1("objects", objects_1.default);
              exports_1("utility", utility_1.default);
              VERSION = '1.0.0';
              exports_1("VERSION", VERSION);
          }
      };
    });
    

    As we can see, in SystemJS the dependencies are declare in the context of a closure:

    var arrays_1, collections_1, functions_1, objects_1, utility_1;
    

    And __moduleName local variable provides a fully normalized name of the current module which can be useful for dynamically loading modules relative to the current module via:

    SystemJS.import('./local-module', __moduleName);
    

    And the callback returns an object that contains a reference to that context and a set of setter functions to allow its modification:

    return {
          setters: [
              function (arrays_1_1) {
                  arrays_1 = arrays_1_1;
              },
              function (collections_1_1) {
                  collections_1 = collections_1_1;
              },
              function (functions_1_1) {
                  functions_1 = functions_1_1;
              },
              function (objects_1_1) {
                  objects_1 = objects_1_1;
              },
              function (utility_1_1) {
                  utility_1 = utility_1_1;
              }
          ],
          execute: function () {
              exports_1("arrays", arrays_1.default);
              exports_1("collections", collections_1.default);
              exports_1("functions", functions_1.default);
              exports_1("objects", objects_1.default);
              exports_1("utility", utility_1.default);
              VERSION = '1.0.0';
              exports_1("VERSION", VERSION);
          }
      };
    

Source Code

Declaring SystemJS modules

See also

Please refer to the recipes about ES6 modules to learn about the declaration of external modules. Refer to chapter one to learn more about Gulp.


https://www.shivkushwaha.com

Shiv Kushwaha

Author/Programmer