Configurable module loader enabling backwards compatibility workflows for ES modules in browsers. If you're interested in modern workflows for ES module compatible browsers only, see ES Module Shims.
Release Links:
SystemJS is currently sponsored by Canopy Tax.
For discussion, join the Gitter Room.
SystemJS provides two hookable base builds:
The minimal 1.5KB s.js loader provides a workflow where code written for production workflows of native ES modules in browsers (like Rollup code-splitting builds), can be transpiled to the System.register module format to work in older browsers that don't supporting native modules, including IE11++.
Since the ES module semantics such as live bindings, circular references, contextual metadata, dynamic import and top-level await can all be fully supported this way, while supporting CSP and cross-origin support, this workflow can be relied upon as a polyfill-like path.
- Loads and resolves modules as URLs, throwing for bare specifier names (eg
import 'lodash'
) like the native module loader. - Loads System.register modules.
- Core hookable extensible loader supporting custom extensions.
The 3KB system.js loader loader builds on the s.js core and adds support for upcoming module specifications (currently import maps and Wasm integration with module loading) as well as development and convenience features.
- Support for loading bare specifier names through import maps (formerly package maps, formerly map configuration), loaded via
<script type="systemjs-importmap">
(requires afetch
polyfill for eg IE11). - Includes the global loading extra for loading global scripts, useful for loading library dependencies traditionally loaded with script tags.
- Tracing hooks and registry deletion API for reloading workflows.
- Supports loading Wasm, CSS and JSON module types.
The following pluggable extras can be dropped in with either the s.js or system.js loader:
- AMD loading support (through
Window.define
which is created). - Named exports convenience extension support for global and AMD module formats (
import { x } from './global.js'
instead ofimport G from './global.js'; G.x
) - Named register supports
System.register('name', ...)
named bundles which can then be imported asSystem.import('name')
(as well as AMD named define support) - Transform loader support, using fetch and eval, supporting a hookable
loader.transform
- Use default provides a convenience interop for AMD modules to return the direct AMD
binding instead of a
{ default: amdModule }
object fromSystem.import
.
The following extras are included in system.js loader by default, and can be added to the s.js loader for a smaller tailored footprint:
- Global loading support for loading global scripts and detecting the defined global as the default export. Useful for loading common library scripts from CDN like
System.import('//unpkg.com/lodash')
. - Module Types
.css
,.wasm
,.json
module type loading support in line with the existing modules specifications.
Since all loader features are hookable, custom extensions can be easily made following the same approach as the bundled extras. See the hooks documentation for more information.
npm install systemjs
You can load System.register modules with a script element in your HTML:
<script type="systemjs-module" src="/js/main.js"></script>
<script type="systemjs-module" src="import:name-of-module"></script>
<script src="system.js"></script>
Using an import:
URL the src
attribute in HTML can reference import maps.
You can also dynamically load modules at any time with System.import()
:
System.import('/js/main.js');
where main.js
is a module available in the System.register module format.
For an example of a bundling workflow, see the Rollup Code Splitting starter project - https://github.com/rollup/rollup-starter-code-splitting.
Say main.js
depends on loading 'lodash'
, then we can define an import map:
<script src="system.js"></script>
<script type="systemjs-importmap">
{
"imports": {
"lodash": "https://unpkg.com/[email protected]/lodash.js"
}
}
</script>
<!-- Alternatively:
<script type="systemjs-importmap" src="path/to/map.json">
-->
<script>
System.import('/js/main.js');
</script>
A list of projects that use or work with SystemJS in providing modular browser workflows. Post a PR.
- es-dev-server - A web server for developing without a build step.
- import map overrides - Dynamically inject an import map stored in local storage so that you can override the URL for any module. Can be useful for running development modules on localhost against the server.
- js-env - Collection of development tools providing a unified workflow to write JavaScript for the web, node.js or both at the same time.
- jspm.io -
https://system-dev.jspm.io/[name]
provides a CDN serving npm modules for SystemJS. - jspm.org - Package manager for native modules, using SystemJS for backwards compatibility.
- single-spa - JavaScript framework for front-end microservices.
- systemjs-webpack-interop - npm lib for setting webpack public path and creating webpack configs that work well with SystemJS.
This list can be extended to include third-party loader extensions. Post a PR.
- transform-babel Supports ES module transformation into System.register with Babel.
- systemjs-css-extra CSS loader plugin
Code-splitting builds on top of native ES modules, like Rollup offers, are an alternative to the Webpack-style chunking approach - offering a way to utilize the native module loader for loading shared and dynamic chunks instead of using a custom registry and loader as Webpack bundles include. Scope-level optimizations can be performed on ES modules when they are combined, while ensuring no duplicate code is loaded through dynamic loading and code-sharing in the module registry, using the features of the native module loader and its dynamic runtime nature.
systemjs-webpack-interop is a community-maintained npm library that might help you get webpack and systemjs working well together.
As of [email protected], it is now possible to compile webpack bundles to System.register format, by modifying your webpack config:
{
output: {
libraryTarget: 'system',
}
}
If building code using the System
global in Webpack, the following config is needed to avoid rewriting:
{
module: {
rules: [
{ parser: { system: false } }
]
}
}
Both builds of SystemJS need Promises in the environment to work, which aren't supported in older browsers like IE11.
Promises can be conditionally polyfilled using, for example, Bluebird (generally the fastest Promise polyfill):
<script>
if (typeof Promise === 'undefined')
document.write('<script src="node_modules/bluebird/js/browser/bluebird.core.js"><\/script>');
</script>
Generally
document.write
is not recommended when writing web applications, but for this use case it works really well and will only apply in older browsers anyway.
To support import maps in the system.js build, a fetch polyfill is need. The GitHub polyfill is recommended:
<script>
if (typeof fetch === 'undefined')
document.write('<script src="node_modules/whatwg-fetch/fetch.js"><\/script>');
</script>
If using CSS modules, a Constructable Stylesheets polyfill is needed - see the module types documentation for further info.
Project bug fixes and changes are welcome for discussion, provided the project footprint remains minimal.
To run the tests:
npm run build && npm run test
For the changelog, see CHANGELOG.md.
MIT