7.3 KiB
Module Formats
The following module formats are supported:
esm: ECMAScript Module (previously referred to ases6)cjs: CommonJSamd: Asynchronous Module Definitionglobal: Global shim module formatregister: System.register or System.registerDynamic module format
The module format can be set via meta configuration:
System.config({
meta: {
'./module/path.js': {
format: 'es6'
}
}
});
By default when not set, automatic regular-expression-based detection is used.
Note that ES6 modules are detected via the presence of
importandexportmodule syntax and no other features at all. This is because the transpilation applies to the module format specifically, not the language.
It is typically advisable to set the module format where possible.
Inter-Format Dependencies
Any module type can be loaded from any other type with full support thanks to zebra-striping.
When loading CommonJS, AMD or Global modules from within ES6, the full module is available at the default export which can be loaded with the default import syntax.
For convenience, named exports are also auto-populated but may not be correctly bound as expected, so use these carefully.
./app/es6-loading-commonjs:
// entire underscore instance
import _ from './underscore.js';
// unbound named export
import {map} from './underscore.js';
ES6
ES6 modules are automatically transpiled as they are loaded, using the loader transpiler option set.
Circular references and bindings are implemented to the ES6 specification.
The __moduleName local variable is also available, pending clarification of the module meta in the WhatWG loader spec.
This provides the fully normalized name of the current module which can be useful for dynamically loading modules relative to the current module via:
System.import('./local-module', __moduleName);
In due course this will be entirely replaced by the contextual loader once this has been specified.
ES6 is loaded via XHR making it non-CSP compatible. ES6 should always be built for production to avoid transpiler costs, making this a development-only feature.
CommonJS
- The
module,exports,require,global,__dirnameand__filenamevariables are all provided. module.idis set.
When executing CommonJS any global define is temporarily removed.
For comprehensive handling of NodeJS modules, a conversion process is needed to make them SystemJS-compatible, such as the one used by jspm.
CommonJS is loaded via XHR making it non-CSP compatible.
AMD
- AMD support includes all AMD structural variations including the CommonJS wrapper form.
- The special
module,exports, andrequiremodule names are handled at the AMD format level and are not defined in the primary loader registry.module.uriandmodule.idare provided withmodule.configas a no-op. - Named defines are supported and will write directly into the loader registry.
- A single named define will write into the loader registry but also be treated as the value of the module loaded if the names do not match. This enables loading a module containing
define('jquery', .... - Contextual dynamic requires are fully supported (
define(function(require) { require(['./dynamic/require'], callback) }))
When executing AMD, the global module, exports are temporarily removed, and the global define and require are set to the SystemJS AMD functions.
By default AMD modules are loaded via <script> tag injection making them CSP-compatible.
RequireJS Support
To use SystemJS side-by-side in a RequireJS project, make sure to include RequireJS after ES6 Module Loader but before SystemJS.
Conversely, to have SystemJS provide a RequireJS-like API in an application set:
window.define = System.amdDefine;
window.require = window.requirejs = System.amdRequire;
Globals
The global format loads globals identically to if they were included via <script> tags
but with some extra features including the ability to shim dependencies,
set custom globals, and define the exports of the global module.
By default, the exports of a global are calculated as the diff of the environment global from before to after execution.
This provides a convenient mechanism for auto-conversion of globals into modules.
For example:
var MyGlobal = 42;
Will get converted into the module Module({ default: 42 }).
While the script:
(function(global) {
global.globalA = 'global A';
global.globalB = 'global B';
})(typeof self != 'undefined' ? self : global);
Will get converted into the module Module({ globalA: 'global A', globalB: 'global B' })
Globals are picked up by variable assignment and undeclared assignment:
var x = 'global'; // detected as a global
y = 'global'; // detected as a global
These two cases fail in IE8, so do need to have their exports explicitly declared if compatibility is desired.
Globals are not removed from the global object for shim compatibility, but this could become possible in future if all globals use the globals meta for shims instead of deps.
Shim Dependencies
When loading plugins of globals like Angular or jQuery plugins, we always need to shim the dependencies of the plugin to be dependent on the global it expects to find.
We do this via deps metadata on the module:
System.config({
meta: {
'vendor/angular-ui-router.js': {
deps: ['/vendor/angular.js']
}
}
});
System.import('vendor/angular-ui-router.js');
Note that deps is only supported for global modules.
It is always advisable to explicitly shim global modules as above for any globals they expect to be present. For example, the above module may work fine without the shim if Angular always happens to load first in the page, but this isn't always guaranteed, and problems will likely be hit later on when the load order happens to change.
Custom Globals
When shimming dependencies, the issue with this is that every dependency needs to be a global in order to be loadable by a global.
This holds the entire ecosystem back as globals become the lowest common denominator.
If we want to upgrade Angular to an ES6 version of Angular, while still supporting old Angular global modules, we can do this via custom globals:
System.config({
meta: {
'vendor/angular-ui-router.js': {
globals: {
angular: 'vendor/angular.js'
}
}
}
});
System.import('vendor/angular-ui-router.js');
In the above scenario, a globally scoped angular will be set to the module value for the Angular ES6 module only for the duration of execution of the global plugin.
Exports
When automatic detection of exports is not enough, a custom exports meta value can be set.
This is a member expression on the global object to be taken as the exports of the module.
For example, angular or jQuery.fn.pluginName.